1.0 Setup

1.1 Import Libraries

library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
library(lubridate)

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
# set ggplot theme
theme_set(theme_light())

1.2 Import Data

df <- readr::read_csv('employee_attrition.csv')
Rows: 49653 Columns: 18
── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (13): recorddate_key, birthdate_key, orighiredate_key, terminationdate_key, city_name, department_name, job_ti...
dbl  (5): EmployeeID, age, length_of_service, store_name, STATUS_YEAR

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(df, 20)

The date variables seems to be in a format unfamiliar to R. This will be corrected in the preprocessing stage. The gender variable is split into 2 columns, gender_full and gender_short. Only 1 is needed so I’ll drop one of them.

2.0 Preprocessing

df <- df %>%
  mutate(
    recorddate_key = as.Date(recorddate_key, '%m/%d/%Y'),
    birthdate_key = as.Date(birthdate_key, '%m/%d/%Y'),
    orighiredate_key = as.Date(orighiredate_key, '%m/%d/%Y'),
    terminationdate_key = as.Date(terminationdate_key, '%m/%d/%Y'),
    termreason_desc = ifelse(termreason_desc == 'Resignaton',
                             'Resignation',
                             termreason_desc),
    city_name = ifelse(city_name == 'New Westminister',
                       'New Westminster',
                       city_name),
    store_name = as.factor(store_name)) %>%
    select(-gender_short)

# df with 1 record for each employee
df2 <- df %>%
  group_by(EmployeeID) %>%
  filter(STATUS_YEAR == max(STATUS_YEAR)) %>%
  filter(!(n() > 1 & termreason_desc == 'Not Applicable')) %>%
  ungroup()

Here I’m converting the date columns to Date type and fixed some typos. I also made a new dataframe containing only the latest record of each employee, which will make working with the data easier.

df2

Here is the new dataframe.

3.0 Univariate Analysis

3.1 Number of Employees

df %>% count(EmployeeID) %>% nrow()
[1] 6284

The Number of distinct employees is same as the number of rows in the new data frame. This validates the data transformation in the previous section.

3.2 Date Variables

Record Date

summary(df$recorddate_key)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
\2006-01-01\ \2008-12-31\ \2011-12-31\ \2011-08-06\ \2013-12-31\ \2015-12-31\ 

Birth Date

summary(df$birthdate_key)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
\1941-01-15\ \1958-05-28\ \1968-12-04\ \1969-01-09\ \1979-07-18\ \1994-12-31\ 

Original Hire Date

summary(df$orighiredate_key)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
\1989-08-28\ \1995-06-02\ \2000-03-31\ \2000-09-04\ \2005-10-13\ \2013-12-11\ 

Termination Date

summary(df$terminationdate_key)
        Min.      1st Qu.       Median         Mean      3rd Qu.         Max. 
\1900-01-01\ \1900-01-01\ \1900-01-01\ \1916-05-10\ \1900-01-01\ \2015-12-30\ 

The date 1900-01-01 in the this column means the employee is still active (still working).

3.3 Age

Age Summary

summary(df2$age)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  19.00   32.00   45.00   44.74   58.00   65.00 
df2 %>%
  ggplot(aes(age)) +
  geom_histogram(binwidth = 2) + 
  scale_x_continuous(breaks = seq(20, 70, 5)) +
  labs(
    title = 'Distribution of Employee Age',
    x = 'Age',
    y = 'Count'
  )

Age distribution seems uniform, with an unexpectedly high number of employees above the age of 60.

3.4 Length of Service (Tenure)

Length of Service Summary

summary(df2$length_of_service)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    7.00   13.00   12.84   19.00   26.00 
df2 %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(0, 30, 1)) +
  labs(
    title = 'Distribution of Employee Tenure',
    x = 'Tenure',
    y = 'Count'
  )

13 years tenure is the most common among the employees here.

3.5 City Name

df2 %>%
  count(city_name) %>%
  ggplot(aes(n, reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

df2 %>% count(city_name) %>% arrange(desc(n))

3.6 Department Name

df2 %>%
  count(department_name) %>%
  ggplot(aes(n, reorder(department_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Department',
    x = 'Number of Employees',
    y = 'Department Name'
  )

df2 %>% count(department_name) %>% arrange(desc(n))

The top 6 departments have the most employees. These departments are under the stores business unit, which in general should have more employees than the head offices.

3.7 Job Title

df2 %>%
  count(job_title) %>%
  ggplot(aes(n, reorder(job_title, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Job Title',
    x = 'Number of Employees',
    y = 'Job Title'
  )

df2 %>% count(job_title) %>% arrange(desc(n))

Same as the previous sections, the jobs with more employees belong to the stores departments.

3.8 Store Name

df2 %>%
  count(store_name) %>%
  ggplot(aes(n, reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

df2 %>% count(store_name) %>% arrange(desc(n))

Some stores have very little employee data (some less than 10). The cause of this might be an issue with the data colletion, or some other reason. This might affect analyses using this column, so I will exclude some of the stores before that. The excluded stores will be the ones with employees less than the 1st quantile value of the column.

3.9 Gender

df2 %>%
  count(gender_full) %>%
  ggplot(aes(n, reorder(gender_full, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Gender',
    x = 'Number of Employees',
    y = 'Gender'
  )

df2 %>% count(gender_full) %>% arrange(desc(n))

3.10 Termination Reason

df2 %>%
  count(termreason_desc) %>%
  ggplot(aes(n, reorder(termreason_desc, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Termination Reason',
    x = 'Number of Employees',
    y = 'Termination Reason'
  )

df2 %>% count(termreason_desc) %>% arrange(desc(n))

Most of the employees are still active. For the later analyses, I will be focusing mostly on the terminated employee data.

3.11 Termination Type

df2 %>%
  count(termtype_desc) %>%
  ggplot(aes(n, reorder(termtype_desc, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Termination Type',
    x = 'Number of Employees',
    y = 'Termination Type'
  )

df2 %>% count(termtype_desc) %>% arrange(desc(n))

Here I’m checking for inconsistent data. There is no voluntary layoffs and involuntary retirements & resignations, so the data is correct here.

3.12 Status

df2 %>%
  count(STATUS) %>%
  ggplot(aes(n, reorder(STATUS, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Employee by Status',
    x = 'Number of Employees',
    y = 'Status'
  )

df2 %>% count(STATUS) %>% arrange(desc(n))

3.13 Status Year

df %>%
  count(STATUS_YEAR) %>%
  ggplot(aes(n, as.factor(STATUS_YEAR))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Records by Status Year',
    x = 'Count',
    y = 'Status Year'
  )

df %>% count(STATUS_YEAR) %>% arrange(desc(n))

The records are collected from 2006 to 2015, although there are some records in the data that start way earlier. I’m assuming that this data have been collected since before 2006, but it has just started being compiled in that year. Or another explanation could be that this data is just a subset of a much larger dataset.

3.14 Business Unit

df2 %>%
  ggplot(aes(BUSINESS_UNIT)) +
  geom_bar() +
  labs(
    title = 'Count of Employees by Business Unit',
    x = 'Business Unit',
    y = 'Number of Employees'
  )

df2 %>% count(BUSINESS_UNIT) %>% arrange(desc(n))

4.0 Question 1: What Factors Lead to Employee Termination?

For this question, I will be focusing mostly on employee resignation. Retirement is for aging employees, so there isn’t any need to find out what is making employees retire. Layoffs on the other hand is quite difficult to analyze because they are mostly due to the company’s decision [ref].

4.1 Analyses

4.1.1 Terminations Over The Years

df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(STATUS_YEAR, termreason_desc) %>%
  ggplot(aes(STATUS_YEAR, n, color = termreason_desc)) +
  geom_line() +
  scale_x_continuous(breaks = seq(2006, 2015)) +
  scale_y_continuous(breaks = seq(0, 150, 25)) +
  labs(
    title = 'Count of Terminations Over The Years Grouped by Reason',
    x = 'Year',
    y = 'Termination Count',
    color = 'Termination Reason'
  )

Layoffs for some reason happened only on 2014.and 2015. This might indicate an incident happened to the company during those 2 years. However, there isn’t enough information in the dataset to further explore why this happened.

4.1.2 Age of Termination

4.1.2.1 Age of Resignations
df2 %>%
  filter(termreason_desc == 'Resignation') %>%
  ggplot(aes(age)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(10, 70, 2)) +
  labs(
    title = 'Distribution of Age of Employee at Resignation',
    x = 'Age',
    y = 'Number of Employees'
  )

Most of the resignations are done by employees between the age of 20 and 30. Age might be one of the factors of resignation. Further analysis on this is needed.

4.1.2.2 Age of Retirements
df2 %>%
  filter(termreason_desc == 'Retirement') %>%
  ggplot(aes(age)) +
  geom_histogram() +
  labs(
    title = 'Distribution of Age of Employee at Retirement',
    x = 'Age',
    y = 'Number of Employees'
  )

As expected, only old people retire.

4.1.2.3 Age of Layoffs
df2 %>%
  filter(termreason_desc == 'Layoff') %>%
  ggplot(aes(age)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(10, 70, 2)) +
  labs(
    title = 'Distribution of Age of Employee at Layoff',
    x = 'Age',
    y = 'Number of Employees'
  )

No obvious patterns here. There’s an unusually high number of employees in their 60s being laid off. A possible explanation to this is because the older employees refuse to retire, and the company no longer has the need for them, so they are laid off.

4.1.3 Length of Service (Tenure)

4.1.3.1 Tenure Until Resignation
df2 %>%
  filter(termreason_desc == 'Resignation') %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  labs(
    title = 'Distribution of Tenure of Employee at Resignation',
    x = 'Tenure',
    y = 'Number of Employees'
  )

The distribution for this looks similiar to the distribution for the age of resignation. These 2 variables might be correlated, and a simple explanation for this would be that the older someone is when they resigned, the longer they must have worked. However this isn’t universal so it’s best not to conclude anything to quickly.

df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  ggplot(aes(age, length_of_service, color = termreason_desc)) +
  geom_point() +
  labs(
    title = 'Correlation of Age with Tenure of Terminated Employees',
    x = 'Age',
    y = 'Tenure',
    color = 'Termination Reason'
  )

As seen in the scatter plot above, these 2 variables do show a positive correlation. The pearson correlation coefficient is:

cor(df2$age, df2$length_of_service)
[1] 0.8493077

A high correlation, good to know but this doesn’t help much in answering the original question.

4.1.3.2 Tenure Until Retirement
df2 %>%
  filter(termreason_desc == 'Retirement') %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  labs(
    title = 'Distribution of Tenure of Employee at Retirement',
    x = 'Tenure',
    y = 'Number of Employees'
  )

4.1.3.3 Tenure Until Layoff
df2 %>%
  filter(termreason_desc == 'Layoff') %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  labs(
    title = 'Distribution of Tenure of Employee at Layoff',
    x = 'Tenure',
    y = 'Number of Employees'
  )

Again here, no obvious patterns for layoffs.

4.1.5 Gender

df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(gender_full, termreason_desc) %>%
  ggplot(aes(gender_full, n)) +
  geom_bar(stat = 'identity') +
  facet_wrap(~termreason_desc) +
  labs(
    title = 'Comparison of Employee Gender Grouped by Termination Reason',
    x = 'Gender',
    y = 'Number of Employees'
  )

From the charts above, we can see that female employees are terminated more than male employees. But, since there are more female workers than male, a different approach is needed to analyse the relationship between gender and termination. Further analysis on this will be in the later section.

4.1.6 City

4.1.6.1 Resignations by City
df2 %>%
  filter(termreason_desc == 'Resignation') %>%
  count(city_name) %>%
  ggplot(aes(n, reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Resignations Grouped by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

The chart above is similiar to the counts from section 3.5. This variable doesn’t seem to influence resignation much, or at all.

4.1.6.2 Retirements by City
df2 %>%
  filter(termreason_desc == 'Retirement') %>%
  count(city_name) %>%
  ggplot(aes(n, reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Retirements Grouped by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

This is same as resignations.

4.1.6.3 Layoffs by City
df2 %>%
  filter(termreason_desc == 'Layoff') %>%
  count(city_name) %>%
  ggplot(aes(n, reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Layoffs Grouped by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

On the other hand, the cities here are very different from the previous 2. These cities might influence layoff in a way. Further analysis is needed.

4.1.7 Department

4.1.7.1 Resignations by Department
df2 %>%
  filter(termreason_desc == 'Resignation') %>%
  count(department_name, termreason_desc) %>%
  ggplot(aes(n, reorder(department_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Resignations by Grouped by Department',
    x = 'Number of Employees',
    y = 'Department Name'
  )

The stores unit departments have high resignation counts. This could be a factor in employee termination.

4.1.7.2 Retirements by Department
df2 %>%
  filter(termreason_desc == 'Retirement') %>%
  count(department_name, termreason_desc) %>%
  ggplot(aes(n, reorder(department_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Retirements by Grouped by Department',
    x = 'Number of Employees',
    y = 'Department Name'
  )

The top departments are slightly different from the other two, but the point is still the same as the others.

4.1.7.3 Layoffs by Department
df2 %>%
  filter(termreason_desc == 'Layoff') %>%
  count(department_name, termreason_desc) %>%
  ggplot(aes(n, reorder(department_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Layoffs by Grouped by Department',
    x = 'Number of Employees',
    y = 'Department Name'
  )

Same as the others, with a notable difference being that only employees from the stores business unit is being laid off.

4.1.8 Job Title

4.1.8.1 Resignations by Job
df2 %>%
  filter(termreason_desc == 'Resignation') %>%
  count(job_title, termreason_desc) %>%
  ggplot(aes(n, reorder(job_title, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Resignations by Grouped by Job Title',
    x = 'Number of Employees',
    y = 'Job Title'
  )

Grouping by job title results in a chart similiar to the one with departments as the group. Jobs belong to a department, so the values in the previous section can explained here. Further analysis on this will be in the later section.

4.1.8.2 Retirements by Job
df2 %>%
  filter(termreason_desc == 'Retirement') %>%
  count(job_title, termreason_desc) %>%
  ggplot(aes(n, reorder(job_title, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Retirements by Grouped by Job Title',
    x = 'Number of Employees',
    y = 'Job Title'
  )

4.1.8.3 Layoffs by Job
df2 %>%
  filter(termreason_desc == 'Layoff') %>%
  count(job_title, termreason_desc) %>%
  ggplot(aes(n, reorder(job_title, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Layoffs by Grouped by Job Title',
    x = 'Number of Employees',
    y = 'Job Title'
  )

4.1.9 Business Unit

df2 %>%
  filter(STATUS == 'TERMINATED') %>% 
  count(BUSINESS_UNIT, termreason_desc) %>%
  group_by(BUSINESS_UNIT) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, BUSINESS_UNIT,
             fill = termreason_desc,
             label = paste(np, '%', ' (', n, ')'))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5), size = 3) +
  labs(
    title = 'Ratio of Termination Reasons In Business Units',
    x = 'Percentage',
    y = 'Business Unit',
    fill = 'Termination Reason'
  )

Nothing much here except that almost all of the employees in the head office units end up retiring.

4.1.10 Store

4.1.10.1 Resignations by Store
# some stores have very little employees so I filtered the data to include only 
# the stores with employees above the 1st quantile of employee number.
q1 <- df2 %>% count(store_name) %>% .$n %>% quantile(.25)

store <- df2 %>%
  group_by(store_name) %>%
  filter(n() >= q1) %>%
  ungroup()

store %>%
  filter(termreason_desc == 'Resignation') %>%
  count(store_name, termreason_desc) %>%
  ggplot(aes(n, reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Resignations Grouped by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

Getting resignation patterns from the store alone is difficult. This variable might not affect resignation much.

4.1.10.2 Retirements by Store
store %>%
  filter(termreason_desc == 'Retirement') %>%
  count(store_name, termreason_desc) %>%
  ggplot(aes(n, reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Retirements Grouped by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

Same as resignation.

4.1.10.3 Layoffs by Store
store %>%
  filter(termreason_desc == 'Layoff') %>%
  count(store_name, termreason_desc) %>%
  ggplot(aes(n, reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Layoffs Grouped by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

Unlike the other termination reasons, layoffs only happen to a few stores as shown in this chart. This might indicate stores being a factor of employee layoffs. Also, this could explain the findings from section 4.1.6.3. Further analysis on this will be on the later section.

4.2 Answer

Based on the above analyses, the factors I have identified are:

  • Age
  • Gender
  • Job & Department
  • Store

From these factors, additional analyses will be done to understand how they are affecting attrition.

5.0 Question 2: How Does Age Affect Employee Attrition?

The relationship between termination age and attrition has been discussed in the previous section. In this section, I will be focusing on the hire age, which is the age at which the employee was hired into the company.

5.1 Analyses

5.1.1 Age During Hire

# new dataframe with employee age during hire
df3 <- df2 %>% mutate(hire_age = age - (STATUS_YEAR - year(orighiredate_key)))

df3 %>%
  ggplot(aes(hire_age)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(19, 55)) +
  labs(
    title = 'Distribution of Age of Employees at Hire',
    x = 'Hire Age',
    y = 'Number of Employees'
  )

Most of the employees were between the age of 20 and 40 at the time of hiring. There’s also a high number of employees aged 51-52, but not many employees in their 40s during hire.

5.1.2 Age During Hire and Termination

5.1.2.1 Termination Reason by Hire Age (percentage)
df3 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(hire_age, termreason_desc) %>%
  group_by(hire_age) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, as.factor(hire_age),
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Hire Ages (Percentage)',
    x = 'Percentage',
    y = 'Hire Age',
    fill = 'Termination Reason'
  )

When comparing the termination reasons with employee age of hire, we can see patterns in data. From the chart above, we can see that employees hired at a younger age tend to resign more. The opposite happens to employees hired at an older age, as they tend to get laid off more (excluding ages 40 and above, which are more likely to retire).

There are several potential causes for this. One of them is the lack of ‘the feeling of accomplishment’ by younger employees. Younger employees want to make an impact ref. Another is the lack of challenge or growth in the workplace. Younger employees tend to get bored with their jobs quickly if it doesn’t provide them with the opportunities to showcase their skills and abilities ref.

There could be other factors in play here such as the relationship with their employers or coworkers, or low salaries, but it is difficult to determine their significance using the current dataset.

5.1.2.1 Termination Reason by Hire Age (count)
df3 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(hire_age, termreason_desc) %>%
  ggplot(aes(n, as.factor(hire_age), fill = termreason_desc, label = n)) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Hire Ages (Number of Employees)',
    x = 'Percentage',
    y = 'Hire Age',
    fill = 'Termination Reason'
  )

5.1.3 Correlation Between Age of Hire and Tenure

df3 %>%
  filter(STATUS == 'TERMINATED') %>%
  ggplot(aes(hire_age, length_of_service, color = termreason_desc)) +
  geom_point() +
  labs(
    title = 'Correlation between Hire Age and Tenure',
    x = 'Hire Age',
    y = 'Tenure',
    color = 'Termination Reason'
  )

Same as section 4.1.3), hire age also has correlation with tenure. The correlation coefficient this time is:

cor(df3$hire_age, df3$length_of_service)
[1] 0.5851923

Which is lower than with termination age, but can still be considered a high value. Hire age is a more useful variable as it can be used to predict the expected employee tenure.

5.2 Answer

Based on the above analyses, it can be concluded that age, more specifically the age during hire, can influence employee attrition. Younger employees are more likely to resign due to reasons such as the boredom or the lack of acomplishment. Other than that, the hire age can also be used to predict employee tenure.

5.3 Reccomendations

Since the employees that are most likely to leave the company are younger ones, it should benefit the company to focus more on them. Since this is a retail company, it might be difficult to give them a sense of achievement or more challenges.

One thing they can do is to give them more responsibilities. The company can assigned a few employees to handle an event such as a limited time event, which can be specific to a store or a city. This can provide employees the chance for them to show their skills, and also give them the engagement needed to not get bored working there.

6.0 Question 3: How Does Jobs Affect Employee Attrition

Refering to the analyses at section 4.1.8, there are several jobs that have higher number of terminations. In this section, I will do deeper analyses to try to understand why this happened.

6.1 Analyses

6.1.1 Ratio of Termination Reasons by Job

6.1.1.1 Resignations by Job (percentage)
df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(job_title, termreason_desc) %>%
  group_by(job_title) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, reorder(job_title, n),
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Job Titles (Percentage)',
    x = 'Percentage',
    y = 'Job Title',
    fill = 'Termination Reason'
  )

By looking at the ratio of termination reasons in each job, you can see that cashiers and shelf stockers have quite a lot more resignations compared to retirements and layoffs. Following these 2 are dairy persons and bakers. Meat cutters and produce clerks have the most retirements, both in count and in ratio.

From this chart alone, it can be inferred that specific jobs, like cashiers, can be held accountable for termination. Before coming to this conclusion, I will verify first that the department is not responsible for this, since jobs belong to departments. I’ll need to check if the jobs of interest belong to the same department or not.

6.1.1.2 Resignations by Job (count)
df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(job_title, termreason_desc) %>%
  ggplot(aes(n, reorder(job_title, n), fill = termreason_desc, label = n)) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Job Titles (Count)',
    x = 'Percentage',
    y = 'Job Title',
    fill = 'Termination Reason'
  )

6.1.2 Ratio of Termination Reasons by Department

6.1.2.1 Resignations by Department (percentage)
df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(department_name, termreason_desc) %>%
  group_by(department_name) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, reorder(department_name, n),
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Departments (Percentage)',
    x = 'Percentage',
    y = 'Department Name',
    fill = 'Termination Reason'
  )

Here are some checks to help verify the previous section’s statements. The charts here look similiar to the previous section’s. The ratio of termination reasons doesn’t look far off as well. It can be concluded that the main factor here is the jobs themselves, not including the departments.

p.s: Some of the departments have very little employees. These are the departments under the head office business unit. The ones with a lot of employees are from the stores business unit.

6.1.2.2 Resignations by Department (count)
df2 %>%
  filter(STATUS == 'TERMINATED') %>%
  count(department_name, termreason_desc) %>%
  ggplot(aes(n, reorder(department_name, n), fill = termreason_desc, label = n)) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Termination Reasons in Departments (Count)',
    x = 'Percentage',
    y = 'Department Name',
    fill = 'Termination Reason'
  )

6.1.3 Jobs and Their Parent Departments

df2 %>% count(department_name, job_title)

It can be seen from this table that the top 6 jobs from section 6.1.1 is in different departments. I will analyze cashiers and shelf stockers specificaly, to find out if there is something in common to them that might lead to resignation.

6.1.4 Further Analysis on Cashiers and Shelf Stockers

6.1.4.1 Cashiers
6.1.4.1.1 Age
cashiers <- df2 %>% filter(job_title == 'Cashier')

cashiers %>%
  ggplot(aes(age)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(20, 70, 5)) +
  labs(
    title = 'Distribution of Termination Age of Cashiers',
    x = 'Age',
    y = 'Number of Employees'
  )

Nothing stands out here. These results look similiar to previous analyses. One minor thing to point out is that there are slightly more males here.

6.1.4.1.2 Length of Service
cashiers %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(0, 25, 5)) +
  labs(
    title = 'Distribution of Tenure of Cashiers',
    x = 'Tenure',
    y = 'Number of Employees'
  )

6.1.4.1.3 Gender
cashiers %>%
  count(gender_full) %>%
  ggplot(aes(n, gender_full)) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Cashiers by Gender',
    x = 'Number of Employees',
    y = 'Gender'
  )

6.1.4.1.4 City
cashiers %>%
  count(city_name) %>%
  ggplot(aes(x = n, y = reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Cashiers Grouped by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

6.1.4.1.5 Store
store %>%
  filter(job_title == 'Cashier') %>%
  count(store_name) %>%
  ggplot(aes(x = n, y = reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Cashiers Grouped by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

6.1.4.2 Shelf Stocker
6.1.4.2.1 Age
stockers <- df2 %>% filter(job_title == 'Shelf Stocker')

stockers %>%
  ggplot(aes(age)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(20, 70, 5)) +
  labs(
    title = 'Distribution of Termination Age of Shelf Stockers',
    x = 'Age',
    y = 'Number of Employees'
  )

The results here are quite normal too. However, just like cashiers, there are more male employees than females. This might mean that men are more likely to resign, but more analysis is needed to verify this claim. It will be done in the later section.

6.1.4.2.2 Length of Service
stockers %>%
  ggplot(aes(length_of_service)) +
  geom_histogram(binwidth = 1) +
  scale_x_continuous(breaks = seq(0, 25, 5)) +
  labs(
    title = 'Distribution of Tenure of Shelf Stockers',
    x = 'Tenure',
    y = 'Number of Employees'
  )

6.1.4.2.3 Gender
stockers %>%
  count(gender_full) %>%
  ggplot(aes(n, gender_full)) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Shelf Stockers by Gender',
    x = 'Number of Employees',
    y = 'Gender'
  )

6.1.4.2.4 City
stockers %>%
  count(city_name) %>%
  ggplot(aes(x = n, y = reorder(city_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Shelf Stockers Grouped by City',
    x = 'Number of Employees',
    y = 'City Name'
  )

6.1.4.2.5 Store
store %>%
  filter(job_title == 'Shelf Stocker') %>%
  count(store_name) %>%
  ggplot(aes(x = n, y = reorder(store_name, n))) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Count of Shelf Stockers Grouped by Store',
    x = 'Number of Employees',
    y = 'Store Name'
  )

6.2 Answer

Based on these analyses only, there doesn’t seem to be anything that could explain why some jobs have higher termination rates than others. However, this can be explained by including job satisfaction and salary in to the equation.

This dataset contains data of employees from British Columbia (BC), Canada, so I will be gathering external data specific to this region. Cashiers and shelf stockers in BC have below average salary and job satisfaction. The average living cost in BC is $1,839 a month for 1 person and the average salary after taxes is $3,048 ref.

The average salaries in BC are $2,559 a month for cashiers (ref) and $2,600 a month for shelf stockers (ref). Both are lower than the average, which may cause employees to quit their jobs to find a better paying one. Also cashiers deal with stressful working conditions often such as rude customers and terrible bosses. This can also cause them to look for a job with better working conditions.

6.3 Reccomendations

A simple solution that can help to reduce resignations in these jobs is to increase the salary. However, it is not reccommended to be raised more than $3,000, the average, as any more would not be beneficial for the company. Also raising the salary doesn’t help fix the job dissatisfaction. For that, a fix could be to improve the working conditions. It can be implemented in several ways such as providing free lunch or snacks, more break times, giving awards, etc.

7.0 Question 4: How Does Gender Affect Employee Attrition?

This section aims to answer the questions from section 4.1.5 and section 6.1.4.

7.1 Analyses

7.1.1 Ratio of Termination Reasons by Gender

df2 %>%
  filter(STATUS == 'TERMINATED') %>% 
  count(gender_full, termreason_desc) %>%
  group_by(gender_full) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(x = n, y = gender_full,
             fill = termreason_desc,
             label = paste(np, '% (', n, ')', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5), size = 3) +
  labs(
    title = 'Ratio of Termination Reasons in Each Gender',
    x = 'Percentage',
    y = 'Gender',
    fill = 'Termination Reason'
  )

The ratio for both male and female is quite similiar, with females being a little bit more ‘loyal’ to the company, because of the larger ratio of retirements over resignations and layoffs.

7.1.2 Tenure by Gender

df2 %>%
  group_by(gender_full) %>%
  summarise(avg_tenure = sum(length_of_service) / n()) %>%
  ggplot(aes(gender_full, avg_tenure)) +
  geom_bar(stat = 'identity') +
  labs(
    title = 'Average Tenure of Employees Grouped by Gender',
    x = 'Gender',
    y = 'Average Tenure'
  )

This backs up the previous section’s statement, as the average length of service of females is a little bit longer than males.

7.1.3 Age of Hire Distribution by Gender

df3 %>%
  ggplot(aes(hire_age, gender_full, fill = gender_full)) +
  geom_violin() +
  labs(
    title = 'Distribution of Hire Age Grouped by Gender',
    x = 'Hire Age',
    y = 'Gender',
    fill = 'Gender'
  )

Nothing interesting here. The distributions are similiar to the ones in section 5.1.1. Gender and hire age doesn’t seem to be correlated.

7.1.4 Termination Reasons of Gender Grouped by Job

7.1.4.1 Cashiers and Shelf Stockers
df2 %>%
  filter(STATUS == 'TERMINATED' &
         job_title %in% c('Cashier', 'Shelf Stocker')) %>% 
  count(gender_full, termreason_desc, job_title) %>%
  group_by(gender_full) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(x = n, y = gender_full,
             fill = termreason_desc,
             label = paste(np, '% (', n, ')', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5), size = 3) +
  facet_wrap(~job_title, ncol = 1) +
  labs(
    title = 'Ratio of Cashier & Shelf Stocker Termination Reasons by Gender',
    x = 'Percentage',
    y = 'Gender',
    fill = 'Termination Reason'
  )

Male cashiers are slightly more likely to resign than female cashiers. Shelf stockers however, have more female employee resignations than male. This means that gender might not be affecting resignation. Shelf stocker ratios are the oposite of cashiers.

7.1.4.2 Top 10 Jobs by Number of Employees
df2 %>%
  filter(STATUS == 'TERMINATED') %>% 
  filter(job_title %in% (
    df2 %>% count(job_title) %>% arrange(desc(n)) %>% head(10) %>% .$job_title
  )) %>%
  count(gender_full, termreason_desc, job_title) %>%
  group_by(job_title, gender_full) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, gender_full,
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') +
  geom_text(position = position_fill(vjust = 0.5), size = 3) +
  facet_wrap(~job_title) +
  labs(
    title = 'Ratio of Termination Reasons by Gender of Top 10 Jobs',
    x = 'Percentage',
    y = 'Gender',
    fill = 'Termination Reason'
  )

There doesn’t seem to be any noticable patterns in resignation of both genders in different jobs. This analysis further strengthens the claim in the previous section about gender not influencing resignation. However, most of the jobs have more male employee layoffs then female. This is align with the analysis in section 7.1.1.

7.2 Answer

Gender doesn’t seem to affect termination much, but based on these analyses, in general female employees are less likely to resign or be laid off. It is difficult to explain why this could have happened from the given data alone. Reasons for this was discussed in a survey conducted by LinkedIn (ref).

7.3 Reccomendations

8.0 Question 5: What Causes Employee Layoff?

The layoffs in the company happened only at the year 2014 and 2015, so there isn’t a lot of data on layoffs. Some clues are found in section 4.1.10.3, and will be analyzed further here, but it is difficult to say just from the data alone, what makes an employee more likely to be laid off.

8.1 Analyses

8.1.1 Employee Status by Store

8.1.1.1 Ratio of Employee Status by Store (percentage)
store %>%
  group_by(store_name, termreason_desc) %>%
  count() %>%
  group_by(store_name) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, reorder(store_name, n),
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') + 
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Active & Terminated Employees by Store (Percentage)',
    x = 'Percentage',
    y = 'Store Name',
    fill = 'Termination Reason'
  )

This analysis is the continuation from section 4.1.10. Just like before, the stores listed here have been filtered to exclude stores with very few employees. We can see that stores 11, 13, and 20 have the largest ratio of layoffs. Also, they do not have any more active employees. This could mean that the store has shut down, or they have stopped sharing their employee data. The data provided is not enough to explain this.

The same goes to store 37, with over 90% of their employees retired, and the rest laid off. Store 35 might be soon following as only around 6% of their employees are still active.

8.1.1.2 Ratio of Employee Status by Store (count)
store %>%
  group_by(store_name, termreason_desc) %>%
  count() %>%
  ggplot(aes(n, reorder(store_name, n), fill = termreason_desc, label = n)) +
  geom_bar(stat = 'identity', position = 'fill') + 
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Active & Terminated Employees by Store (Count)',
    x = 'Percentage',
    y = 'Store Name',
    fill = 'Termination Reason'
  )

8.1.2 Employee Terminations by Store

8.1.2.1 Ratio of Termination Reasons by Store (percentage)
store %>%
  filter(STATUS == 'TERMINATED') %>%
  group_by(store_name, termreason_desc) %>%
  count() %>%
  group_by(store_name) %>%
  mutate(ng = sum(n),
         np = round(n / ng, 2) * 100) %>%
  ggplot(aes(n, reorder(store_name, n),
             fill = termreason_desc,
             label = paste(np, '%', sep = ''))) +
  geom_bar(stat = 'identity', position = 'fill') + 
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Terminated Reasons by Store (Percentage)',
    x = 'Percentage',
    y = 'Store Name',
    fill = 'Termination Reason'
  )

This is the chart from the previous section, excluding the active employees. The stores with 100% resignation rate is interesting, and further analysis on them might lead to new insights, but for this section I will be focusing only on layoffs. That means I will narrow down the analysis to just 3 stores: 11, 13, and 20.

8.1.2.2 Ratio of Termination Reasons by Store (count)
store %>%
  filter(STATUS == 'TERMINATED') %>%
  group_by(store_name, termreason_desc) %>%
  count() %>%
  ggplot(aes(n, reorder(store_name, n), fill = termreason_desc, label = n)) +
  geom_bar(stat = 'identity', position = 'fill') + 
  geom_text(position = position_fill(vjust = 0.5)) +
  labs(
    title = 'Ratio of Terminated Reasons by Store (Count)',
    x = 'Percentage',
    y = 'Store Name',
    fill = 'Termination Reason'
  )

8.1.3 Which Cities Are The Stores In?

df2 %>%
  group_by(city_name) %>%
  summarise(stores = toString(unique(store_name))) %>%
  arrange(desc(stringr::str_length(stores)))

The stores of interest are all from different cities, so city is most likely not a factor here.

8.1.4 Hire Age Distribution in Certain Stores

# dataframe with only stores 11, 13, 20
certain_store <- store %>% filter(store_name %in% c(11, 13, 20))

df3 %>%
  filter(STATUS == 'TERMINATED' & store_name %in% certain_store$store_name) %>%
  ggplot(aes(hire_age)) +
  geom_histogram(binwidth = 1) +
  facet_wrap(~store_name, ncol = 1) +
  labs(
    title = 'Distribution of Hire Age of Certain Stores',
    x = 'Hire Age',
    y = 'Number of Employees'
  )

Again, nothing unusual here.

8.1.5 Gender Ratio in Certain Stores

8.1.5.1 Gender Ratio in Certain Stores (percentage)
certain_store%>%
  ggplot(aes(gender_full)) + 
  geom_bar() +
  facet_wrap(~store_name)

Gender distribution in these stores don’t seem to follow a pattern.

8.2 Answer

Based on the analyses in this section, the conclusion I’ve reached is that the reasons for layoffs cannot be determined from the provided data alone. The most probable reason now is the closing of several stores. This statement can be backed by this article from indeed [ref] which states business closing as one of the reasons for employee layoff.

LS0tCnRpdGxlOiAiUllBTiBNQVJUSU4gLSBUUDA1ODA5MSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgIyB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKPCEtLSAKVGhpcyBmaWxlIGNvbnRhaW5zIHRoZSBmdWxsIGFuYWx5c2lzIGZvciB0aGlzIGFzc2lnbm1lbnQuIFRvIGdldCBvbmx5IAp0aGUgY29kZSBpbmNsdWRlZCBpbiB0aGUgd29yZCBkb2N1bWVudCwgY2hlY2sgdGhlIFJTY3JpcHQgZmlsZS4KVGhlIGh0bWwgZG9jdW1lbnQgZ2VuZXJhdGVkIGlzIGFsc28gaW5jbHVkZWQgaGVyZSBzbyB0aGVyZSdzIG5vIG5lZWQgdG8gcmVuZGVyIAppdCBtYW51YWxseS4KLS0+CgojIyAxLjAgU2V0dXAKYGBge3IgaW5jbHVkZSA9IEZBTFNFfQojIGRvbid0IGluY2x1ZGUgbWVzc2FnZXMKa25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgojIyMgMS4xIEltcG9ydCBMaWJyYXJpZXMKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsdWJyaWRhdGUpCgojIHNldCBnZ3Bsb3QgdGhlbWUKdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpCmBgYAoKIyMjIDEuMiBJbXBvcnQgRGF0YQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KZGYgPC0gcmVhZHI6OnJlYWRfY3N2KCdlbXBsb3llZV9hdHRyaXRpb24uY3N2JykKaGVhZChkZiwgMjApCmBgYApUaGUgZGF0ZSB2YXJpYWJsZXMgc2VlbXMgdG8gYmUgaW4gYSBmb3JtYXQgdW5mYW1pbGlhciB0byBSLiBUaGlzIHdpbGwgYmUgY29ycmVjdGVkIAppbiB0aGUgcHJlcHJvY2Vzc2luZyBzdGFnZS4gVGhlIGdlbmRlciB2YXJpYWJsZSBpcyBzcGxpdCBpbnRvIDIgY29sdW1ucywgYGdlbmRlcl9mdWxsYAphbmQgYGdlbmRlcl9zaG9ydGAuIE9ubHkgMSBpcyBuZWVkZWQgc28gSSdsbCBkcm9wIG9uZSBvZiB0aGVtLgoKIyMgMi4wIFByZXByb2Nlc3NpbmcKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmRmIDwtIGRmICU+JQogIG11dGF0ZSgKICAgIHJlY29yZGRhdGVfa2V5ID0gYXMuRGF0ZShyZWNvcmRkYXRlX2tleSwgJyVtLyVkLyVZJyksCiAgICBiaXJ0aGRhdGVfa2V5ID0gYXMuRGF0ZShiaXJ0aGRhdGVfa2V5LCAnJW0vJWQvJVknKSwKICAgIG9yaWdoaXJlZGF0ZV9rZXkgPSBhcy5EYXRlKG9yaWdoaXJlZGF0ZV9rZXksICclbS8lZC8lWScpLAogICAgdGVybWluYXRpb25kYXRlX2tleSA9IGFzLkRhdGUodGVybWluYXRpb25kYXRlX2tleSwgJyVtLyVkLyVZJyksCiAgICB0ZXJtcmVhc29uX2Rlc2MgPSBpZmVsc2UodGVybXJlYXNvbl9kZXNjID09ICdSZXNpZ25hdG9uJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVzaWduYXRpb24nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm1yZWFzb25fZGVzYyksCiAgICBjaXR5X25hbWUgPSBpZmVsc2UoY2l0eV9uYW1lID09ICdOZXcgV2VzdG1pbmlzdGVyJywKICAgICAgICAgICAgICAgICAgICAgICAnTmV3IFdlc3RtaW5zdGVyJywKICAgICAgICAgICAgICAgICAgICAgICBjaXR5X25hbWUpLAogICAgc3RvcmVfbmFtZSA9IGFzLmZhY3RvcihzdG9yZV9uYW1lKSkgJT4lCiAgICBzZWxlY3QoLWdlbmRlcl9zaG9ydCkKCiMgZGYgd2l0aCAxIHJlY29yZCBmb3IgZWFjaCBlbXBsb3llZQpkZjIgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoRW1wbG95ZWVJRCkgJT4lCiAgZmlsdGVyKFNUQVRVU19ZRUFSID09IG1heChTVEFUVVNfWUVBUikpICU+JQogIGZpbHRlcighKG4oKSA+IDEgJiB0ZXJtcmVhc29uX2Rlc2MgPT0gJ05vdCBBcHBsaWNhYmxlJykpICU+JQogIHVuZ3JvdXAoKQpgYGAKSGVyZSBJJ20gY29udmVydGluZyB0aGUgZGF0ZSBjb2x1bW5zIHRvIERhdGUgdHlwZSBhbmQgZml4ZWQgc29tZSB0eXBvcy4gSSBhbHNvIAptYWRlIGEgbmV3IGRhdGFmcmFtZSBjb250YWluaW5nIG9ubHkgdGhlIGxhdGVzdCByZWNvcmQgb2YgZWFjaCBlbXBsb3llZSwgd2hpY2ggCndpbGwgbWFrZSB3b3JraW5nIHdpdGggdGhlIGRhdGEgZWFzaWVyLgoKCmBgYHtyfQpkZjIKYGBgCkhlcmUgaXMgdGhlIG5ldyBkYXRhZnJhbWUuCgojIyAzLjAgVW5pdmFyaWF0ZSBBbmFseXNpcyB7LnRhYnNldH0KIyMjIDMuMSBOdW1iZXIgb2YgRW1wbG95ZWVzCmBgYHtyfQpkZiAlPiUgY291bnQoRW1wbG95ZWVJRCkgJT4lIG5yb3coKQpgYGAKVGhlIE51bWJlciBvZiBkaXN0aW5jdCBlbXBsb3llZXMgaXMgc2FtZSBhcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIG5ldyBkYXRhIApmcmFtZS4gVGhpcyB2YWxpZGF0ZXMgdGhlIGRhdGEgdHJhbnNmb3JtYXRpb24gaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24uCgojIyMgMy4yIERhdGUgVmFyaWFibGVzCioqUmVjb3JkIERhdGUqKgpgYGB7cn0Kc3VtbWFyeShkZiRyZWNvcmRkYXRlX2tleSkKYGBgCioqQmlydGggRGF0ZSoqCmBgYHtyfQpzdW1tYXJ5KGRmJGJpcnRoZGF0ZV9rZXkpCmBgYAoqKk9yaWdpbmFsIEhpcmUgRGF0ZSoqCmBgYHtyfQpzdW1tYXJ5KGRmJG9yaWdoaXJlZGF0ZV9rZXkpCmBgYAoqKlRlcm1pbmF0aW9uIERhdGUqKgpgYGB7cn0Kc3VtbWFyeShkZiR0ZXJtaW5hdGlvbmRhdGVfa2V5KQpgYGAKVGhlIGRhdGUgYDE5MDAtMDEtMDFgIGluIHRoZSB0aGlzIGNvbHVtbiBtZWFucyB0aGUgZW1wbG95ZWUgaXMgc3RpbGwgYWN0aXZlIAooc3RpbGwgd29ya2luZykuCgojIyMgMy4zIEFnZQoqKkFnZSBTdW1tYXJ5KioKYGBge3J9CnN1bW1hcnkoZGYyJGFnZSkKYGBgCmBgYHtyfQpkZjIgJT4lCiAgZ2dwbG90KGFlcyhhZ2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAyKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsIDcwLCA1KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgRW1wbG95ZWUgQWdlJywKICAgIHggPSAnQWdlJywKICAgIHkgPSAnQ291bnQnCiAgKQoKYGBgCkFnZSBkaXN0cmlidXRpb24gc2VlbXMgdW5pZm9ybSwgd2l0aCBhbiB1bmV4cGVjdGVkbHkgaGlnaCBudW1iZXIgb2YgZW1wbG95ZWVzIAphYm92ZSB0aGUgYWdlIG9mIDYwLgoKIyMjIDMuNCBMZW5ndGggb2YgU2VydmljZSAoVGVudXJlKQoqKkxlbmd0aCBvZiBTZXJ2aWNlIFN1bW1hcnkqKgpgYGB7cn0Kc3VtbWFyeShkZjIkbGVuZ3RoX29mX3NlcnZpY2UpCmBgYApgYGB7cn0KZGYyICU+JQogIGdncGxvdChhZXMobGVuZ3RoX29mX3NlcnZpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAzMCwgMSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnRGlzdHJpYnV0aW9uIG9mIEVtcGxveWVlIFRlbnVyZScsCiAgICB4ID0gJ1RlbnVyZScsCiAgICB5ID0gJ0NvdW50JwogICkKYGBgCjEzIHllYXJzIHRlbnVyZSBpcyB0aGUgbW9zdCBjb21tb24gYW1vbmcgdGhlIGVtcGxveWVlcyBoZXJlLgoKIyMjIDMuNSBDaXR5IE5hbWUKYGBge3J9CmRmMiAlPiUKICBjb3VudChjaXR5X25hbWUpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihjaXR5X25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIEVtcGxveWVlIGJ5IENpdHknLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnQ2l0eSBOYW1lJwogICkKCmRmMiAlPiUgY291bnQoY2l0eV9uYW1lKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKCiMjIyAzLjYgRGVwYXJ0bWVudCBOYW1lCmBgYHtyfQpkZjIgJT4lCiAgY291bnQoZGVwYXJ0bWVudF9uYW1lKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoZGVwYXJ0bWVudF9uYW1lLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBFbXBsb3llZSBieSBEZXBhcnRtZW50JywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0RlcGFydG1lbnQgTmFtZScKICApCgpkZjIgJT4lIGNvdW50KGRlcGFydG1lbnRfbmFtZSkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgClRoZSB0b3AgNiBkZXBhcnRtZW50cyBoYXZlIHRoZSBtb3N0IGVtcGxveWVlcy4gVGhlc2UgZGVwYXJ0bWVudHMgYXJlIHVuZGVyIHRoZSAKc3RvcmVzIGJ1c2luZXNzIHVuaXQsIHdoaWNoIGluIGdlbmVyYWwgc2hvdWxkIGhhdmUgbW9yZSBlbXBsb3llZXMgdGhhbiB0aGUgCmhlYWQgb2ZmaWNlcy4KCiMjIyAzLjcgSm9iIFRpdGxlCmBgYHtyfQpkZjIgJT4lCiAgY291bnQoam9iX3RpdGxlKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoam9iX3RpdGxlLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBFbXBsb3llZSBieSBKb2IgVGl0bGUnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnSm9iIFRpdGxlJwogICkKCmRmMiAlPiUgY291bnQoam9iX3RpdGxlKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKU2FtZSBhcyB0aGUgcHJldmlvdXMgc2VjdGlvbnMsIHRoZSBqb2JzIHdpdGggbW9yZSBlbXBsb3llZXMgYmVsb25nIHRvIHRoZSBzdG9yZXMgCmRlcGFydG1lbnRzLgoKIyMjIDMuOCBTdG9yZSBOYW1lCmBgYHtyfQpkZjIgJT4lCiAgY291bnQoc3RvcmVfbmFtZSkgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKHN0b3JlX25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIEVtcGxveWVlIGJ5IFN0b3JlJywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ1N0b3JlIE5hbWUnCiAgKQoKZGYyICU+JSBjb3VudChzdG9yZV9uYW1lKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKU29tZSBzdG9yZXMgaGF2ZSB2ZXJ5IGxpdHRsZSBlbXBsb3llZSBkYXRhIChzb21lIGxlc3MgdGhhbiAxMCkuIFRoZSBjYXVzZSBvZiB0aGlzCm1pZ2h0IGJlIGFuIGlzc3VlIHdpdGggdGhlIGRhdGEgY29sbGV0aW9uLCBvciBzb21lIG90aGVyIHJlYXNvbi4gVGhpcyBtaWdodCBhZmZlY3QgCmFuYWx5c2VzIHVzaW5nIHRoaXMgY29sdW1uLCBzbyBJIHdpbGwgZXhjbHVkZSBzb21lIG9mIHRoZSBzdG9yZXMgYmVmb3JlIHRoYXQuIFRoZSAKZXhjbHVkZWQgc3RvcmVzIHdpbGwgYmUgdGhlIG9uZXMgd2l0aCBlbXBsb3llZXMgbGVzcyB0aGFuIHRoZSAxc3QgcXVhbnRpbGUgdmFsdWUgCm9mIHRoZSBjb2x1bW4uCgojIyMgMy45IEdlbmRlcgpgYGB7cn0KZGYyICU+JQogIGNvdW50KGdlbmRlcl9mdWxsKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoZ2VuZGVyX2Z1bGwsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIEVtcGxveWVlIGJ5IEdlbmRlcicsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdHZW5kZXInCiAgKQoKZGYyICU+JSBjb3VudChnZW5kZXJfZnVsbCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgojIyMgMy4xMCBUZXJtaW5hdGlvbiBSZWFzb24KYGBge3J9CmRmMiAlPiUKICBjb3VudCh0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcih0ZXJtcmVhc29uX2Rlc2MsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIEVtcGxveWVlIGJ5IFRlcm1pbmF0aW9uIFJlYXNvbicsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQoKZGYyICU+JSBjb3VudCh0ZXJtcmVhc29uX2Rlc2MpICU+JSBhcnJhbmdlKGRlc2MobikpCmBgYApNb3N0IG9mIHRoZSBlbXBsb3llZXMgYXJlIHN0aWxsIGFjdGl2ZS4gRm9yIHRoZSBsYXRlciBhbmFseXNlcywgSSB3aWxsIGJlIGZvY3VzaW5nIAptb3N0bHkgb24gdGhlIHRlcm1pbmF0ZWQgZW1wbG95ZWUgZGF0YS4KCiMjIyAzLjExIFRlcm1pbmF0aW9uIFR5cGUKYGBge3J9CmRmMiAlPiUKICBjb3VudCh0ZXJtdHlwZV9kZXNjKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIodGVybXR5cGVfZGVzYywgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgRW1wbG95ZWUgYnkgVGVybWluYXRpb24gVHlwZScsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdUZXJtaW5hdGlvbiBUeXBlJwogICkKCmRmMiAlPiUgY291bnQodGVybXR5cGVfZGVzYykgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCkhlcmUgSSdtIGNoZWNraW5nIGZvciBpbmNvbnNpc3RlbnQgZGF0YS4gVGhlcmUgaXMgbm8gdm9sdW50YXJ5IGxheW9mZnMgYW5kCmludm9sdW50YXJ5IHJldGlyZW1lbnRzICYgcmVzaWduYXRpb25zLCBzbyB0aGUgZGF0YSBpcyBjb3JyZWN0IGhlcmUuIAoKIyMjIDMuMTIgU3RhdHVzCmBgYHtyfQpkZjIgJT4lCiAgY291bnQoU1RBVFVTKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoU1RBVFVTLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBFbXBsb3llZSBieSBTdGF0dXMnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnU3RhdHVzJwogICkKCmRmMiAlPiUgY291bnQoU1RBVFVTKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKCiMjIyAzLjEzIFN0YXR1cyBZZWFyCmBgYHtyfQpkZiAlPiUKICBjb3VudChTVEFUVVNfWUVBUikgJT4lCiAgZ2dwbG90KGFlcyhuLCBhcy5mYWN0b3IoU1RBVFVTX1lFQVIpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFJlY29yZHMgYnkgU3RhdHVzIFllYXInLAogICAgeCA9ICdDb3VudCcsCiAgICB5ID0gJ1N0YXR1cyBZZWFyJwogICkKCmRmICU+JSBjb3VudChTVEFUVVNfWUVBUikgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgClRoZSByZWNvcmRzIGFyZSBjb2xsZWN0ZWQgZnJvbSAyMDA2IHRvIDIwMTUsIGFsdGhvdWdoIHRoZXJlIGFyZSBzb21lIHJlY29yZHMgaW4gCnRoZSBkYXRhIHRoYXQgc3RhcnQgd2F5IGVhcmxpZXIuIEknbSBhc3N1bWluZyB0aGF0IHRoaXMgZGF0YSBoYXZlIGJlZW4gY29sbGVjdGVkIApzaW5jZSBiZWZvcmUgMjAwNiwgYnV0IGl0IGhhcyBqdXN0IHN0YXJ0ZWQgYmVpbmcgY29tcGlsZWQgaW4gdGhhdCB5ZWFyLiBPciBhbm90aGVyIApleHBsYW5hdGlvbiBjb3VsZCBiZSB0aGF0IHRoaXMgZGF0YSBpcyBqdXN0IGEgc3Vic2V0IG9mIGEgbXVjaCBsYXJnZXIgZGF0YXNldC4KCiMjIyAzLjE0IEJ1c2luZXNzIFVuaXQKYGBge3J9CmRmMiAlPiUKICBnZ3Bsb3QoYWVzKEJVU0lORVNTX1VOSVQpKSArCiAgZ2VvbV9iYXIoKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIEVtcGxveWVlcyBieSBCdXNpbmVzcyBVbml0JywKICAgIHggPSAnQnVzaW5lc3MgVW5pdCcsCiAgICB5ID0gJ051bWJlciBvZiBFbXBsb3llZXMnCiAgKQoKZGYyICU+JSBjb3VudChCVVNJTkVTU19VTklUKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKCiMjIDQuMCBRdWVzdGlvbiAxOiBXaGF0IEZhY3RvcnMgTGVhZCB0byBFbXBsb3llZSBUZXJtaW5hdGlvbj8KRm9yIHRoaXMgcXVlc3Rpb24sIEkgd2lsbCBiZSBmb2N1c2luZyBtb3N0bHkgb24gZW1wbG95ZWUgcmVzaWduYXRpb24uIFJldGlyZW1lbnQgCmlzIGZvciBhZ2luZyBlbXBsb3llZXMsIHNvIHRoZXJlIGlzbid0IGFueSBuZWVkIHRvIGZpbmQgb3V0IHdoYXQgaXMgbWFraW5nIAplbXBsb3llZXMgcmV0aXJlLiBMYXlvZmZzIG9uIHRoZSBvdGhlciBoYW5kIGlzIHF1aXRlIGRpZmZpY3VsdCB0byBhbmFseXplIGJlY2F1c2UgCnRoZXkgYXJlIG1vc3RseSBkdWUgdG8gdGhlIGNvbXBhbnkncyBkZWNpc2lvbiBbW3JlZl1dKGh0dHBzOi8vd3d3LmluZGVlZC5jb20vY2FyZWVyLWFkdmljZS9jYXJlZXItZGV2ZWxvcG1lbnQvcmVhc29ucy1mb3ItbGF5b2ZmcykuIAoKIyMjIDQuMSBBbmFseXNlcwojIyMjIDQuMS4xIFRlcm1pbmF0aW9ucyBPdmVyIFRoZSBZZWFycwpgYGB7cn0KZGYyICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUKICBjb3VudChTVEFUVVNfWUVBUiwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBnZ3Bsb3QoYWVzKFNUQVRVU19ZRUFSLCBuLCBjb2xvciA9IHRlcm1yZWFzb25fZGVzYykpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgyMDA2LCAyMDE1KSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTUwLCAyNSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgVGVybWluYXRpb25zIE92ZXIgVGhlIFllYXJzIEdyb3VwZWQgYnkgUmVhc29uJywKICAgIHggPSAnWWVhcicsCiAgICB5ID0gJ1Rlcm1pbmF0aW9uIENvdW50JywKICAgIGNvbG9yID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYApMYXlvZmZzIGZvciBzb21lIHJlYXNvbiBoYXBwZW5lZCBvbmx5IG9uIDIwMTQuYW5kIDIwMTUuIFRoaXMgbWlnaHQgaW5kaWNhdGUgYW4gCmluY2lkZW50IGhhcHBlbmVkIHRvIHRoZSBjb21wYW55IGR1cmluZyB0aG9zZSAyIHllYXJzLiBIb3dldmVyLCB0aGVyZSBpc24ndCBlbm91Z2ggCmluZm9ybWF0aW9uIGluIHRoZSBkYXRhc2V0IHRvIGZ1cnRoZXIgZXhwbG9yZSB3aHkgdGhpcyBoYXBwZW5lZC4KCiMjIyMgNC4xLjIgQWdlIG9mIFRlcm1pbmF0aW9uIHsudGFic2V0fQojIyMjIyA0LjEuMi4xIEFnZSBvZiBSZXNpZ25hdGlvbnMKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXNpZ25hdGlvbicpICU+JQogIGdncGxvdChhZXMoYWdlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTAsIDcwLCAyKSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgQWdlIG9mIEVtcGxveWVlIGF0IFJlc2lnbmF0aW9uJywKICAgIHggPSAnQWdlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApNb3N0IG9mIHRoZSByZXNpZ25hdGlvbnMgYXJlIGRvbmUgYnkgZW1wbG95ZWVzIGJldHdlZW4gdGhlIGFnZSBvZiAyMCBhbmQgMzAuIEFnZSAKbWlnaHQgYmUgb25lIG9mIHRoZSBmYWN0b3JzIG9mIHJlc2lnbmF0aW9uLiBGdXJ0aGVyIGFuYWx5c2lzIG9uIHRoaXMgaXMgbmVlZGVkLgoKIyMjIyMgNC4xLjIuMiBBZ2Ugb2YgUmV0aXJlbWVudHMKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXRpcmVtZW50JykgJT4lCiAgZ2dwbG90KGFlcyhhZ2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBBZ2Ugb2YgRW1wbG95ZWUgYXQgUmV0aXJlbWVudCcsCiAgICB4ID0gJ0FnZScsCiAgICB5ID0gJ051bWJlciBvZiBFbXBsb3llZXMnCiAgKQpgYGAKQXMgZXhwZWN0ZWQsIG9ubHkgb2xkIHBlb3BsZSByZXRpcmUuCgojIyMjIyA0LjEuMi4zIEFnZSBvZiBMYXlvZmZzCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnTGF5b2ZmJykgJT4lCiAgZ2dwbG90KGFlcyhhZ2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMCwgNzAsIDIpKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBBZ2Ugb2YgRW1wbG95ZWUgYXQgTGF5b2ZmJywKICAgIHggPSAnQWdlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApObyBvYnZpb3VzIHBhdHRlcm5zIGhlcmUuIFRoZXJlJ3MgYW4gdW51c3VhbGx5IGhpZ2ggbnVtYmVyIG9mIGVtcGxveWVlcyBpbiB0aGVpciAKNjBzIGJlaW5nIGxhaWQgb2ZmLiBBIHBvc3NpYmxlIGV4cGxhbmF0aW9uIHRvIHRoaXMgaXMgYmVjYXVzZSB0aGUgb2xkZXIgZW1wbG95ZWVzIApyZWZ1c2UgdG8gcmV0aXJlLCBhbmQgdGhlIGNvbXBhbnkgbm8gbG9uZ2VyIGhhcyB0aGUgbmVlZCBmb3IgdGhlbSwgc28gdGhleSBhcmUgCmxhaWQgb2ZmLgoKIyMjIyA0LjEuMyBMZW5ndGggb2YgU2VydmljZSAoVGVudXJlKSB7LnRhYnNldH0KIyMjIyMgNC4xLjMuMSBUZW51cmUgVW50aWwgUmVzaWduYXRpb24KYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXNpZ25hdGlvbicpICU+JQogIGdncGxvdChhZXMobGVuZ3RoX29mX3NlcnZpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBUZW51cmUgb2YgRW1wbG95ZWUgYXQgUmVzaWduYXRpb24nLAogICAgeCA9ICdUZW51cmUnLAogICAgeSA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJwogICkKYGBgClRoZSBkaXN0cmlidXRpb24gZm9yIHRoaXMgbG9va3Mgc2ltaWxpYXIgdG8gdGhlIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIGFnZSBvZiAKcmVzaWduYXRpb24uIFRoZXNlIDIgdmFyaWFibGVzIG1pZ2h0IGJlIGNvcnJlbGF0ZWQsIGFuZCBhIHNpbXBsZSBleHBsYW5hdGlvbiBmb3IgCnRoaXMgd291bGQgYmUgdGhhdCB0aGUgb2xkZXIgc29tZW9uZSBpcyB3aGVuIHRoZXkgcmVzaWduZWQsIHRoZSBsb25nZXIgdGhleSBtdXN0IApoYXZlIHdvcmtlZC4gSG93ZXZlciB0aGlzIGlzbid0IHVuaXZlcnNhbCBzbyBpdCdzIGJlc3Qgbm90IHRvIGNvbmNsdWRlIGFueXRoaW5nIAp0byBxdWlja2x5LgoKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIoU1RBVFVTID09ICdURVJNSU5BVEVEJykgJT4lCiAgZ2dwbG90KGFlcyhhZ2UsIGxlbmd0aF9vZl9zZXJ2aWNlLCBjb2xvciA9IHRlcm1yZWFzb25fZGVzYykpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3JyZWxhdGlvbiBvZiBBZ2Ugd2l0aCBUZW51cmUgb2YgVGVybWluYXRlZCBFbXBsb3llZXMnLAogICAgeCA9ICdBZ2UnLAogICAgeSA9ICdUZW51cmUnLAogICAgY29sb3IgPSAnVGVybWluYXRpb24gUmVhc29uJwogICkKYGBgCkFzIHNlZW4gaW4gdGhlIHNjYXR0ZXIgcGxvdCBhYm92ZSwgdGhlc2UgMiB2YXJpYWJsZXMgZG8gc2hvdyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uLgpUaGUgcGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBpczoKCmBgYHtyfQpjb3IoZGYyJGFnZSwgZGYyJGxlbmd0aF9vZl9zZXJ2aWNlKQpgYGAKQSBoaWdoIGNvcnJlbGF0aW9uLCBnb29kIHRvIGtub3cgYnV0IHRoaXMgZG9lc24ndCBoZWxwIG11Y2ggaW4gYW5zd2VyaW5nIHRoZSAKb3JpZ2luYWwgcXVlc3Rpb24uCgojIyMjIyA0LjEuMy4yIFRlbnVyZSBVbnRpbCBSZXRpcmVtZW50CmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnUmV0aXJlbWVudCcpICU+JQogIGdncGxvdChhZXMobGVuZ3RoX29mX3NlcnZpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBUZW51cmUgb2YgRW1wbG95ZWUgYXQgUmV0aXJlbWVudCcsCiAgICB4ID0gJ1RlbnVyZScsCiAgICB5ID0gJ051bWJlciBvZiBFbXBsb3llZXMnCiAgKQpgYGAKCiMjIyMjIDQuMS4zLjMgVGVudXJlIFVudGlsIExheW9mZgpgYGB7cn0KZGYyICU+JQogIGZpbHRlcih0ZXJtcmVhc29uX2Rlc2MgPT0gJ0xheW9mZicpICU+JQogIGdncGxvdChhZXMobGVuZ3RoX29mX3NlcnZpY2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBUZW51cmUgb2YgRW1wbG95ZWUgYXQgTGF5b2ZmJywKICAgIHggPSAnVGVudXJlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApBZ2FpbiBoZXJlLCBubyBvYnZpb3VzIHBhdHRlcm5zIGZvciBsYXlvZmZzLgoKCiMjIyMgNC4xLjUgR2VuZGVyCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcpICU+JQogIGNvdW50KGdlbmRlcl9mdWxsLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMoZ2VuZGVyX2Z1bGwsIG4pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBmYWNldF93cmFwKH50ZXJtcmVhc29uX2Rlc2MpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ29tcGFyaXNvbiBvZiBFbXBsb3llZSBHZW5kZXIgR3JvdXBlZCBieSBUZXJtaW5hdGlvbiBSZWFzb24nLAogICAgeCA9ICdHZW5kZXInLAogICAgeSA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJwogICkKYGBgCkZyb20gdGhlIGNoYXJ0cyBhYm92ZSwgd2UgY2FuIHNlZSB0aGF0IGZlbWFsZSBlbXBsb3llZXMgYXJlIHRlcm1pbmF0ZWQgbW9yZSB0aGFuIAptYWxlIGVtcGxveWVlcy4gQnV0LCBzaW5jZSB0aGVyZSBhcmUgbW9yZSBmZW1hbGUgd29ya2VycyB0aGFuIG1hbGUsIGEgZGlmZmVyZW50IAphcHByb2FjaCBpcyBuZWVkZWQgdG8gYW5hbHlzZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZ2VuZGVyIGFuZCB0ZXJtaW5hdGlvbi4gCkZ1cnRoZXIgYW5hbHlzaXMgb24gdGhpcyB3aWxsIGJlIGluIHRoZSBsYXRlciBzZWN0aW9uLgoKIyMjIyA0LjEuNiBDaXR5IHsudGFic2V0fQojIyMjIyA0LjEuNi4xIFJlc2lnbmF0aW9ucyBieSBDaXR5CmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnUmVzaWduYXRpb24nKSAlPiUKICBjb3VudChjaXR5X25hbWUpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihjaXR5X25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFJlc2lnbmF0aW9ucyBHcm91cGVkIGJ5IENpdHknLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnQ2l0eSBOYW1lJwogICkKYGBgClRoZSBjaGFydCBhYm92ZSBpcyBzaW1pbGlhciB0byB0aGUgY291bnRzIGZyb20gc2VjdGlvbiBbMy41XSgjY2l0eS1uYW1lKS4gVGhpcyAKdmFyaWFibGUgZG9lc24ndCBzZWVtIHRvIGluZmx1ZW5jZSByZXNpZ25hdGlvbiBtdWNoLCBvciBhdCBhbGwuCgojIyMjIyA0LjEuNi4yIFJldGlyZW1lbnRzIGJ5IENpdHkKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXRpcmVtZW50JykgJT4lCiAgY291bnQoY2l0eV9uYW1lKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoY2l0eV9uYW1lLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBSZXRpcmVtZW50cyBHcm91cGVkIGJ5IENpdHknLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnQ2l0eSBOYW1lJwogICkKYGBgClRoaXMgaXMgc2FtZSBhcyByZXNpZ25hdGlvbnMuCgojIyMjIyA0LjEuNi4zIExheW9mZnMgYnkgQ2l0eQpgYGB7cn0KZGYyICU+JQogIGZpbHRlcih0ZXJtcmVhc29uX2Rlc2MgPT0gJ0xheW9mZicpICU+JQogIGNvdW50KGNpdHlfbmFtZSkgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKGNpdHlfbmFtZSwgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgTGF5b2ZmcyBHcm91cGVkIGJ5IENpdHknLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnQ2l0eSBOYW1lJwogICkKYGBgCk9uIHRoZSBvdGhlciBoYW5kLCB0aGUgY2l0aWVzIGhlcmUgYXJlIHZlcnkgZGlmZmVyZW50IGZyb20gdGhlIHByZXZpb3VzIDIuIFRoZXNlIApjaXRpZXMgbWlnaHQgaW5mbHVlbmNlIGxheW9mZiBpbiBhIHdheS4gRnVydGhlciBhbmFseXNpcyBpcyBuZWVkZWQuCgojIyMjIDQuMS43IERlcGFydG1lbnQgey50YWJzZXR9CiMjIyMjIDQuMS43LjEgUmVzaWduYXRpb25zIGJ5IERlcGFydG1lbnQKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXNpZ25hdGlvbicpICU+JQogIGNvdW50KGRlcGFydG1lbnRfbmFtZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoZGVwYXJ0bWVudF9uYW1lLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBSZXNpZ25hdGlvbnMgYnkgR3JvdXBlZCBieSBEZXBhcnRtZW50JywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0RlcGFydG1lbnQgTmFtZScKICApCmBgYApUaGUgc3RvcmVzIHVuaXQgZGVwYXJ0bWVudHMgaGF2ZSBoaWdoIHJlc2lnbmF0aW9uIGNvdW50cy4gVGhpcyBjb3VsZCBiZSBhIGZhY3RvciAKaW4gZW1wbG95ZWUgdGVybWluYXRpb24uCgojIyMjIyA0LjEuNy4yIFJldGlyZW1lbnRzIGJ5IERlcGFydG1lbnQKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIodGVybXJlYXNvbl9kZXNjID09ICdSZXRpcmVtZW50JykgJT4lCiAgY291bnQoZGVwYXJ0bWVudF9uYW1lLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihkZXBhcnRtZW50X25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFJldGlyZW1lbnRzIGJ5IEdyb3VwZWQgYnkgRGVwYXJ0bWVudCcsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdEZXBhcnRtZW50IE5hbWUnCiAgKQpgYGAKVGhlIHRvcCBkZXBhcnRtZW50cyBhcmUgc2xpZ2h0bHkgZGlmZmVyZW50IGZyb20gdGhlIG90aGVyIHR3bywgYnV0IHRoZSBwb2ludCBpcyAKc3RpbGwgdGhlIHNhbWUgYXMgdGhlIG90aGVycy4KCiMjIyMjIDQuMS43LjMgTGF5b2ZmcyBieSBEZXBhcnRtZW50CmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnTGF5b2ZmJykgJT4lCiAgY291bnQoZGVwYXJ0bWVudF9uYW1lLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihkZXBhcnRtZW50X25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIExheW9mZnMgYnkgR3JvdXBlZCBieSBEZXBhcnRtZW50JywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0RlcGFydG1lbnQgTmFtZScKICApCmBgYApTYW1lIGFzIHRoZSBvdGhlcnMsIHdpdGggYSBub3RhYmxlIGRpZmZlcmVuY2UgYmVpbmcgdGhhdCBvbmx5IGVtcGxveWVlcyBmcm9tIHRoZSAKc3RvcmVzIGJ1c2luZXNzIHVuaXQgaXMgYmVpbmcgbGFpZCBvZmYuCgojIyMjIDQuMS44IEpvYiBUaXRsZSB7LnRhYnNldH0KIyMjIyMgNC4xLjguMSBSZXNpZ25hdGlvbnMgYnkgSm9iCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnUmVzaWduYXRpb24nKSAlPiUKICBjb3VudChqb2JfdGl0bGUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKGpvYl90aXRsZSwgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgUmVzaWduYXRpb25zIGJ5IEdyb3VwZWQgYnkgSm9iIFRpdGxlJywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0pvYiBUaXRsZScKICApCmBgYApHcm91cGluZyBieSBqb2IgdGl0bGUgcmVzdWx0cyBpbiBhIGNoYXJ0IHNpbWlsaWFyIHRvIHRoZSBvbmUgd2l0aCBkZXBhcnRtZW50cyBhcyAKdGhlIGdyb3VwLiBKb2JzIGJlbG9uZyB0byBhIGRlcGFydG1lbnQsIHNvIHRoZSB2YWx1ZXMgaW4gdGhlIHByZXZpb3VzIHNlY3Rpb24gCmNhbiBleHBsYWluZWQgaGVyZS4gRnVydGhlciBhbmFseXNpcyBvbiB0aGlzIHdpbGwgYmUgaW4gdGhlIGxhdGVyIHNlY3Rpb24uCgojIyMjIyA0LjEuOC4yIFJldGlyZW1lbnRzIGJ5IEpvYgpgYGB7cn0KZGYyICU+JQogIGZpbHRlcih0ZXJtcmVhc29uX2Rlc2MgPT0gJ1JldGlyZW1lbnQnKSAlPiUKICBjb3VudChqb2JfdGl0bGUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKGpvYl90aXRsZSwgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgUmV0aXJlbWVudHMgYnkgR3JvdXBlZCBieSBKb2IgVGl0bGUnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnSm9iIFRpdGxlJwogICkKYGBgCgojIyMjIyA0LjEuOC4zIExheW9mZnMgYnkgSm9iCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnTGF5b2ZmJykgJT4lCiAgY291bnQoam9iX3RpdGxlLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihqb2JfdGl0bGUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIExheW9mZnMgYnkgR3JvdXBlZCBieSBKb2IgVGl0bGUnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnSm9iIFRpdGxlJwogICkKYGBgCgojIyMjIDQuMS45IEJ1c2luZXNzIFVuaXQKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIoU1RBVFVTID09ICdURVJNSU5BVEVEJykgJT4lIAogIGNvdW50KEJVU0lORVNTX1VOSVQsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ3JvdXBfYnkoQlVTSU5FU1NfVU5JVCkgJT4lCiAgbXV0YXRlKG5nID0gc3VtKG4pLAogICAgICAgICBucCA9IHJvdW5kKG4gLyBuZywgMikgKiAxMDApICU+JQogIGdncGxvdChhZXMobiwgQlVTSU5FU1NfVU5JVCwKICAgICAgICAgICAgIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsCiAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKG5wLCAnJScsICcgKCcsIG4sICcpJykpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2ZpbGwnKSArCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSksIHNpemUgPSAzKSArCiAgbGFicygKICAgIHRpdGxlID0gJ1JhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgSW4gQnVzaW5lc3MgVW5pdHMnLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnQnVzaW5lc3MgVW5pdCcsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYApOb3RoaW5nIG11Y2ggaGVyZSBleGNlcHQgdGhhdCBhbG1vc3QgYWxsIG9mIHRoZSBlbXBsb3llZXMgaW4gdGhlIGhlYWQgb2ZmaWNlIAp1bml0cyBlbmQgdXAgcmV0aXJpbmcuCgojIyMjIDQuMS4xMCBTdG9yZSB7LnRhYnNldH0KIyMjIyMgNC4xLjEwLjEgUmVzaWduYXRpb25zIGJ5IFN0b3JlCmBgYHtyfQojIHNvbWUgc3RvcmVzIGhhdmUgdmVyeSBsaXR0bGUgZW1wbG95ZWVzIHNvIEkgZmlsdGVyZWQgdGhlIGRhdGEgdG8gaW5jbHVkZSBvbmx5IAojIHRoZSBzdG9yZXMgd2l0aCBlbXBsb3llZXMgYWJvdmUgdGhlIDFzdCBxdWFudGlsZSBvZiBlbXBsb3llZSBudW1iZXIuCnExIDwtIGRmMiAlPiUgY291bnQoc3RvcmVfbmFtZSkgJT4lIC4kbiAlPiUgcXVhbnRpbGUoLjI1KQoKc3RvcmUgPC0gZGYyICU+JQogIGdyb3VwX2J5KHN0b3JlX25hbWUpICU+JQogIGZpbHRlcihuKCkgPj0gcTEpICU+JQogIHVuZ3JvdXAoKQoKc3RvcmUgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnUmVzaWduYXRpb24nKSAlPiUKICBjb3VudChzdG9yZV9uYW1lLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihzdG9yZV9uYW1lLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBSZXNpZ25hdGlvbnMgR3JvdXBlZCBieSBTdG9yZScsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdTdG9yZSBOYW1lJwogICkKYGBgCkdldHRpbmcgcmVzaWduYXRpb24gcGF0dGVybnMgZnJvbSB0aGUgc3RvcmUgYWxvbmUgaXMgZGlmZmljdWx0LiBUaGlzIHZhcmlhYmxlIAptaWdodCBub3QgYWZmZWN0IHJlc2lnbmF0aW9uIG11Y2guCgojIyMjIyA0LjEuMTAuMiBSZXRpcmVtZW50cyBieSBTdG9yZQpgYGB7cn0Kc3RvcmUgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnUmV0aXJlbWVudCcpICU+JQogIGNvdW50KHN0b3JlX25hbWUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKHN0b3JlX25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFJldGlyZW1lbnRzIEdyb3VwZWQgYnkgU3RvcmUnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnU3RvcmUgTmFtZScKICApCmBgYApTYW1lIGFzIHJlc2lnbmF0aW9uLgoKIyMjIyMgNC4xLjEwLjMgTGF5b2ZmcyBieSBTdG9yZQpgYGB7cn0Kc3RvcmUgJT4lCiAgZmlsdGVyKHRlcm1yZWFzb25fZGVzYyA9PSAnTGF5b2ZmJykgJT4lCiAgY291bnQoc3RvcmVfbmFtZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoc3RvcmVfbmFtZSwgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgTGF5b2ZmcyBHcm91cGVkIGJ5IFN0b3JlJywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ1N0b3JlIE5hbWUnCiAgKQpgYGAKVW5saWtlIHRoZSBvdGhlciB0ZXJtaW5hdGlvbiByZWFzb25zLCBsYXlvZmZzIG9ubHkgaGFwcGVuIHRvIGEgZmV3IHN0b3JlcyBhcyAKc2hvd24gaW4gdGhpcyBjaGFydC4gVGhpcyBtaWdodCBpbmRpY2F0ZSBzdG9yZXMgYmVpbmcgYSBmYWN0b3Igb2YgZW1wbG95ZWUgbGF5b2Zmcy4gCkFsc28sIHRoaXMgY291bGQgZXhwbGFpbiB0aGUgZmluZGluZ3MgZnJvbSBzZWN0aW9uIFs0LjEuNi4zXSgjbGF5b2Zmcy1ieS1jaXR5KS4gCkZ1cnRoZXIgYW5hbHlzaXMgb24gdGhpcyB3aWxsIGJlIG9uIHRoZSBsYXRlciBzZWN0aW9uLgoKIyMjIDQuMiBBbnN3ZXIKQmFzZWQgb24gdGhlIGFib3ZlIGFuYWx5c2VzLCB0aGUgZmFjdG9ycyBJIGhhdmUgaWRlbnRpZmllZCBhcmU6ICAKCiogQWdlCiogR2VuZGVyCiogSm9iICYgRGVwYXJ0bWVudAoqIFN0b3JlCgpGcm9tIHRoZXNlIGZhY3RvcnMsIGFkZGl0aW9uYWwgYW5hbHlzZXMgd2lsbCBiZSBkb25lIHRvIHVuZGVyc3RhbmQgaG93IHRoZXkgYXJlIAphZmZlY3RpbmcgYXR0cml0aW9uLgoKIyMgNS4wIFF1ZXN0aW9uIDI6IEhvdyBEb2VzIEFnZSBBZmZlY3QgRW1wbG95ZWUgQXR0cml0aW9uPwpUaGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGVybWluYXRpb24gYWdlIGFuZCBhdHRyaXRpb24gaGFzIGJlZW4gZGlzY3Vzc2VkIGluIHRoZSAKcHJldmlvdXMgc2VjdGlvbi4gSW4gdGhpcyBzZWN0aW9uLCBJIHdpbGwgYmUgZm9jdXNpbmcgb24gdGhlIGhpcmUgYWdlLCB3aGljaCBpcyAKdGhlIGFnZSBhdCB3aGljaCB0aGUgZW1wbG95ZWUgd2FzIGhpcmVkIGludG8gdGhlIGNvbXBhbnkuCgojIyMgNS4xIEFuYWx5c2VzCiMjIyMgNS4xLjEgQWdlIER1cmluZyBIaXJlCmBgYHtyfQojIG5ldyBkYXRhZnJhbWUgd2l0aCBlbXBsb3llZSBhZ2UgZHVyaW5nIGhpcmUKZGYzIDwtIGRmMiAlPiUgbXV0YXRlKGhpcmVfYWdlID0gYWdlIC0gKFNUQVRVU19ZRUFSIC0geWVhcihvcmlnaGlyZWRhdGVfa2V5KSkpCgpkZjMgJT4lCiAgZ2dwbG90KGFlcyhoaXJlX2FnZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5LCA1NSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnRGlzdHJpYnV0aW9uIG9mIEFnZSBvZiBFbXBsb3llZXMgYXQgSGlyZScsCiAgICB4ID0gJ0hpcmUgQWdlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApNb3N0IG9mIHRoZSBlbXBsb3llZXMgd2VyZSBiZXR3ZWVuIHRoZSBhZ2Ugb2YgMjAgYW5kIDQwIGF0IHRoZSB0aW1lIG9mIGhpcmluZy4gClRoZXJlJ3MgYWxzbyBhIGhpZ2ggbnVtYmVyIG9mIGVtcGxveWVlcyBhZ2VkIDUxLTUyLCBidXQgbm90IG1hbnkgZW1wbG95ZWVzIGluIAp0aGVpciA0MHMgZHVyaW5nIGhpcmUuCgojIyMjIDUuMS4yIEFnZSBEdXJpbmcgSGlyZSBhbmQgVGVybWluYXRpb24gey50YWJzZXR9CiMjIyMjIDUuMS4yLjEgVGVybWluYXRpb24gUmVhc29uIGJ5IEhpcmUgQWdlIChwZXJjZW50YWdlKQpgYGB7cn0KZGYzICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUKICBjb3VudChoaXJlX2FnZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBncm91cF9ieShoaXJlX2FnZSkgJT4lCiAgbXV0YXRlKG5nID0gc3VtKG4pLAogICAgICAgICBucCA9IHJvdW5kKG4gLyBuZywgMikgKiAxMDApICU+JQogIGdncGxvdChhZXMobiwgYXMuZmFjdG9yKGhpcmVfYWdlKSwKICAgICAgICAgICAgIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsCiAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKG5wLCAnJScsIHNlcCA9ICcnKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBUZXJtaW5hdGlvbiBSZWFzb25zIGluIEhpcmUgQWdlcyAoUGVyY2VudGFnZSknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnSGlyZSBBZ2UnLAogICAgZmlsbCA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQpgYGAKV2hlbiBjb21wYXJpbmcgdGhlIHRlcm1pbmF0aW9uIHJlYXNvbnMgd2l0aCBlbXBsb3llZSBhZ2Ugb2YgaGlyZSwgd2UgY2FuIHNlZSAKcGF0dGVybnMgaW4gZGF0YS4gRnJvbSB0aGUgY2hhcnQgYWJvdmUsIHdlIGNhbiBzZWUgdGhhdCBlbXBsb3llZXMgaGlyZWQgYXQgYSAKeW91bmdlciBhZ2UgdGVuZCB0byByZXNpZ24gbW9yZS4gVGhlIG9wcG9zaXRlIGhhcHBlbnMgdG8gZW1wbG95ZWVzIGhpcmVkIGF0IGFuIApvbGRlciBhZ2UsIGFzIHRoZXkgdGVuZCB0byBnZXQgbGFpZCBvZmYgbW9yZSAoZXhjbHVkaW5nIGFnZXMgNDAgYW5kIGFib3ZlLCB3aGljaCAKYXJlIG1vcmUgbGlrZWx5IHRvIHJldGlyZSkuIAoKVGhlcmUgYXJlIHNldmVyYWwgcG90ZW50aWFsIGNhdXNlcyBmb3IgdGhpcy4gT25lIG9mIHRoZW0gaXMgdGhlIGxhY2sgb2YgJ3RoZSBmZWVsaW5nCm9mIGFjY29tcGxpc2htZW50JyBieSB5b3VuZ2VyIGVtcGxveWVlcy4gWW91bmdlciBlbXBsb3llZXMgd2FudCB0byBtYWtlIGFuIGltcGFjdCAKW3JlZl0oaHR0cHM6Ly91d2F0ZXJsb28uY2EvaGlyZS9mb3VyLXJlYXNvbnMteW91bmctZW1wbG95ZWVzLWxlYXZlLWpvYnMpLiBBbm90aGVyIAppcyB0aGUgbGFjayBvZiBjaGFsbGVuZ2Ugb3IgZ3Jvd3RoIGluIHRoZSB3b3JrcGxhY2UuIFlvdW5nZXIgZW1wbG95ZWVzIHRlbmQgdG8gZ2V0IApib3JlZCB3aXRoIHRoZWlyIGpvYnMgcXVpY2tseSBpZiBpdCBkb2Vzbid0IHByb3ZpZGUgdGhlbSB3aXRoIHRoZSBvcHBvcnR1bml0aWVzIAp0byBzaG93Y2FzZSB0aGVpciBza2lsbHMgYW5kIGFiaWxpdGllcyBbcmVmXShodHRwczovL21lZGl1bS5jb20vQHN0YWZmY2lyY2xlL3JlYXNvbnMtd2h5LXlvdW5nLXdvcmtlcnMtbGVhdmUtdGhlaXItam9icy1hbmQtbG9vay1mb3ItbW9yZS1zdGFibGUtd29yay1hZDM1ZTFmZWJjMDEpLgoKVGhlcmUgY291bGQgYmUgb3RoZXIgZmFjdG9ycyBpbiBwbGF5IGhlcmUgc3VjaCBhcyB0aGUgcmVsYXRpb25zaGlwIHdpdGggdGhlaXIgCmVtcGxveWVycyBvciBjb3dvcmtlcnMsIG9yIGxvdyBzYWxhcmllcywgYnV0IGl0IGlzIGRpZmZpY3VsdCB0byBkZXRlcm1pbmUgdGhlaXIgCnNpZ25pZmljYW5jZSB1c2luZyB0aGUgY3VycmVudCBkYXRhc2V0LgoKIyMjIyMgNS4xLjIuMSBUZXJtaW5hdGlvbiBSZWFzb24gYnkgSGlyZSBBZ2UgKGNvdW50KQpgYGB7cn0KZGYzICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUKICBjb3VudChoaXJlX2FnZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIGFzLmZhY3RvcihoaXJlX2FnZSksIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsIGxhYmVsID0gbikpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBUZXJtaW5hdGlvbiBSZWFzb25zIGluIEhpcmUgQWdlcyAoTnVtYmVyIG9mIEVtcGxveWVlcyknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnSGlyZSBBZ2UnLAogICAgZmlsbCA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQpgYGAKCiMjIyMgNS4xLjMgQ29ycmVsYXRpb24gQmV0d2VlbiBBZ2Ugb2YgSGlyZSBhbmQgVGVudXJlCmBgYHtyfQpkZjMgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcpICU+JQogIGdncGxvdChhZXMoaGlyZV9hZ2UsIGxlbmd0aF9vZl9zZXJ2aWNlLCBjb2xvciA9IHRlcm1yZWFzb25fZGVzYykpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3JyZWxhdGlvbiBiZXR3ZWVuIEhpcmUgQWdlIGFuZCBUZW51cmUnLAogICAgeCA9ICdIaXJlIEFnZScsCiAgICB5ID0gJ1RlbnVyZScsCiAgICBjb2xvciA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQpgYGAKU2FtZSBhcyBzZWN0aW9uIFs0LjEuM10oI3RlbnVyZS11bnRpbC1yZXNpZ25hdGlvbikpLCBoaXJlIGFnZSBhbHNvIGhhcyAKY29ycmVsYXRpb24gd2l0aCB0ZW51cmUuIFRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCB0aGlzIHRpbWUgaXM6CmBgYHtyfQpjb3IoZGYzJGhpcmVfYWdlLCBkZjMkbGVuZ3RoX29mX3NlcnZpY2UpCmBgYApXaGljaCBpcyBsb3dlciB0aGFuIHdpdGggdGVybWluYXRpb24gYWdlLCBidXQgY2FuIHN0aWxsIGJlIGNvbnNpZGVyZWQgYSBoaWdoIHZhbHVlLiAKSGlyZSBhZ2UgaXMgYSBtb3JlIHVzZWZ1bCB2YXJpYWJsZSBhcyBpdCBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBleHBlY3RlZCBlbXBsb3llZSB0ZW51cmUuCgojIyMgNS4yIEFuc3dlcgpCYXNlZCBvbiB0aGUgYWJvdmUgYW5hbHlzZXMsIGl0IGNhbiBiZSBjb25jbHVkZWQgdGhhdCBhZ2UsIG1vcmUgc3BlY2lmaWNhbGx5IHRoZSAKYWdlIGR1cmluZyBoaXJlLCBjYW4gaW5mbHVlbmNlIGVtcGxveWVlIGF0dHJpdGlvbi4gWW91bmdlciBlbXBsb3llZXMgYXJlIG1vcmUgCmxpa2VseSB0byByZXNpZ24gZHVlIHRvIHJlYXNvbnMgc3VjaCBhcyB0aGUgYm9yZWRvbSBvciB0aGUgbGFjayBvZiBhY29tcGxpc2htZW50LiAKT3RoZXIgdGhhbiB0aGF0LCB0aGUgaGlyZSBhZ2UgY2FuIGFsc28gYmUgdXNlZCB0byBwcmVkaWN0IGVtcGxveWVlIHRlbnVyZS4KCiMjIyA1LjMgUmVjY29tZW5kYXRpb25zClNpbmNlIHRoZSBlbXBsb3llZXMgdGhhdCBhcmUgbW9zdCBsaWtlbHkgdG8gbGVhdmUgdGhlIGNvbXBhbnkgYXJlIHlvdW5nZXIgb25lcywgCml0IHNob3VsZCBiZW5lZml0IHRoZSBjb21wYW55IHRvIGZvY3VzIG1vcmUgb24gdGhlbS4gU2luY2UgdGhpcyBpcyBhIHJldGFpbCAKY29tcGFueSwgaXQgbWlnaHQgYmUgZGlmZmljdWx0IHRvIGdpdmUgdGhlbSBhIHNlbnNlIG9mIGFjaGlldmVtZW50IG9yIG1vcmUgCmNoYWxsZW5nZXMuIAoKT25lIHRoaW5nIHRoZXkgY2FuIGRvIGlzIHRvIGdpdmUgdGhlbSBtb3JlIHJlc3BvbnNpYmlsaXRpZXMuIFRoZSAKY29tcGFueSBjYW4gYXNzaWduZWQgYSBmZXcgZW1wbG95ZWVzIHRvIGhhbmRsZSBhbiBldmVudCBzdWNoIGFzIGEgbGltaXRlZCB0aW1lIApldmVudCwgd2hpY2ggY2FuIGJlIHNwZWNpZmljIHRvIGEgc3RvcmUgb3IgYSBjaXR5LiBUaGlzIGNhbiBwcm92aWRlIGVtcGxveWVlcyAKdGhlIGNoYW5jZSBmb3IgdGhlbSB0byBzaG93IHRoZWlyIHNraWxscywgYW5kIGFsc28gZ2l2ZSB0aGVtIHRoZSBlbmdhZ2VtZW50IG5lZWRlZCAKdG8gbm90IGdldCBib3JlZCB3b3JraW5nIHRoZXJlLgoKIyMgNi4wIFF1ZXN0aW9uIDM6IEhvdyBEb2VzIEpvYnMgQWZmZWN0IEVtcGxveWVlIEF0dHJpdGlvbgpSZWZlcmluZyB0byB0aGUgYW5hbHlzZXMgYXQgc2VjdGlvbiBbNC4xLjhdKCM0MThfSm9iX1RpdGxlKSwgdGhlcmUgYXJlIHNldmVyYWwgCmpvYnMgdGhhdCBoYXZlIGhpZ2hlciBudW1iZXIgb2YgdGVybWluYXRpb25zLiBJbiB0aGlzIHNlY3Rpb24sIEkgd2lsbCBkbyBkZWVwZXIgCmFuYWx5c2VzIHRvIHRyeSB0byB1bmRlcnN0YW5kIHdoeSB0aGlzIGhhcHBlbmVkLgoKIyMjIDYuMSBBbmFseXNlcwojIyMjIDYuMS4xIFJhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgYnkgSm9iIHsudGFic2V0fQojIyMjIyA2LjEuMS4xIFJlc2lnbmF0aW9ucyBieSBKb2IgKHBlcmNlbnRhZ2UpCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcpICU+JQogIGNvdW50KGpvYl90aXRsZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBncm91cF9ieShqb2JfdGl0bGUpICU+JQogIG11dGF0ZShuZyA9IHN1bShuKSwKICAgICAgICAgbnAgPSByb3VuZChuIC8gbmcsIDIpICogMTAwKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoam9iX3RpdGxlLCBuKSwKICAgICAgICAgICAgIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsCiAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKG5wLCAnJScsIHNlcCA9ICcnKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBUZXJtaW5hdGlvbiBSZWFzb25zIGluIEpvYiBUaXRsZXMgKFBlcmNlbnRhZ2UpJywKICAgIHggPSAnUGVyY2VudGFnZScsCiAgICB5ID0gJ0pvYiBUaXRsZScsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYApCeSBsb29raW5nIGF0IHRoZSByYXRpbyBvZiB0ZXJtaW5hdGlvbiByZWFzb25zIGluIGVhY2ggam9iLCB5b3UgY2FuIHNlZSB0aGF0IApjYXNoaWVycyBhbmQgc2hlbGYgc3RvY2tlcnMgaGF2ZSBxdWl0ZSBhIGxvdCBtb3JlIHJlc2lnbmF0aW9ucyBjb21wYXJlZCB0byAKcmV0aXJlbWVudHMgYW5kIGxheW9mZnMuIEZvbGxvd2luZyB0aGVzZSAyIGFyZSBkYWlyeSBwZXJzb25zIGFuZCBiYWtlcnMuIE1lYXQgCmN1dHRlcnMgYW5kIHByb2R1Y2UgY2xlcmtzIGhhdmUgdGhlIG1vc3QgcmV0aXJlbWVudHMsIGJvdGggaW4gY291bnQgYW5kIGluIHJhdGlvLgoKRnJvbSB0aGlzIGNoYXJ0IGFsb25lLCBpdCBjYW4gYmUgaW5mZXJyZWQgdGhhdCBzcGVjaWZpYyBqb2JzLCBsaWtlIGNhc2hpZXJzLCBjYW4gCmJlIGhlbGQgYWNjb3VudGFibGUgZm9yIHRlcm1pbmF0aW9uLiBCZWZvcmUgY29taW5nIHRvIHRoaXMgY29uY2x1c2lvbiwgSSB3aWxsIAp2ZXJpZnkgZmlyc3QgdGhhdCB0aGUgZGVwYXJ0bWVudCBpcyBub3QgcmVzcG9uc2libGUgZm9yIHRoaXMsIHNpbmNlIGpvYnMgYmVsb25nIAp0byBkZXBhcnRtZW50cy4gSSdsbCBuZWVkIHRvIGNoZWNrIGlmIHRoZSBqb2JzIG9mIGludGVyZXN0IGJlbG9uZyB0byB0aGUgc2FtZSAKZGVwYXJ0bWVudCBvciBub3QuCgojIyMjIyA2LjEuMS4yIFJlc2lnbmF0aW9ucyBieSBKb2IgKGNvdW50KQpgYGB7cn0KZGYyICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUKICBjb3VudChqb2JfdGl0bGUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKGpvYl90aXRsZSwgbiksIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsIGxhYmVsID0gbikpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBUZXJtaW5hdGlvbiBSZWFzb25zIGluIEpvYiBUaXRsZXMgKENvdW50KScsCiAgICB4ID0gJ1BlcmNlbnRhZ2UnLAogICAgeSA9ICdKb2IgVGl0bGUnLAogICAgZmlsbCA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQpgYGAKCiMjIyMgNi4xLjIgUmF0aW8gb2YgVGVybWluYXRpb24gUmVhc29ucyBieSBEZXBhcnRtZW50IHsudGFic2V0fQojIyMjIyA2LjEuMi4xIFJlc2lnbmF0aW9ucyBieSBEZXBhcnRtZW50IChwZXJjZW50YWdlKQpgYGB7cn0KZGYyICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUKICBjb3VudChkZXBhcnRtZW50X25hbWUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgZ3JvdXBfYnkoZGVwYXJ0bWVudF9uYW1lKSAlPiUKICBtdXRhdGUobmcgPSBzdW0obiksCiAgICAgICAgIG5wID0gcm91bmQobiAvIG5nLCAyKSAqIDEwMCkgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKGRlcGFydG1lbnRfbmFtZSwgbiksCiAgICAgICAgICAgICBmaWxsID0gdGVybXJlYXNvbl9kZXNjLAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZShucCwgJyUnLCBzZXAgPSAnJykpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2ZpbGwnKSArCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmF0aW8gb2YgVGVybWluYXRpb24gUmVhc29ucyBpbiBEZXBhcnRtZW50cyAoUGVyY2VudGFnZSknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnRGVwYXJ0bWVudCBOYW1lJywKICAgIGZpbGwgPSAnVGVybWluYXRpb24gUmVhc29uJwogICkKYGBgCkhlcmUgYXJlIHNvbWUgY2hlY2tzIHRvIGhlbHAgdmVyaWZ5IHRoZSBwcmV2aW91cyBzZWN0aW9uJ3Mgc3RhdGVtZW50cy4gVGhlIGNoYXJ0cyAKaGVyZSBsb29rIHNpbWlsaWFyIHRvIHRoZSBwcmV2aW91cyBzZWN0aW9uJ3MuIFRoZSByYXRpbyBvZiB0ZXJtaW5hdGlvbiByZWFzb25zIApkb2Vzbid0IGxvb2sgZmFyIG9mZiBhcyB3ZWxsLiBJdCBjYW4gYmUgY29uY2x1ZGVkIHRoYXQgdGhlIG1haW4gZmFjdG9yIGhlcmUgaXMgCnRoZSBqb2JzIHRoZW1zZWx2ZXMsIG5vdCBpbmNsdWRpbmcgdGhlIGRlcGFydG1lbnRzLgoKcC5zOgpTb21lIG9mIHRoZSBkZXBhcnRtZW50cyBoYXZlIHZlcnkgbGl0dGxlIGVtcGxveWVlcy4gVGhlc2UgYXJlIHRoZSBkZXBhcnRtZW50cyAKdW5kZXIgdGhlIGhlYWQgb2ZmaWNlIGJ1c2luZXNzIHVuaXQuIFRoZSBvbmVzIHdpdGggYSBsb3Qgb2YgZW1wbG95ZWVzIGFyZSBmcm9tIAp0aGUgc3RvcmVzIGJ1c2luZXNzIHVuaXQuCgojIyMjIyA2LjEuMi4yIFJlc2lnbmF0aW9ucyBieSBEZXBhcnRtZW50IChjb3VudCkKYGBge3J9CmRmMiAlPiUKICBmaWx0ZXIoU1RBVFVTID09ICdURVJNSU5BVEVEJykgJT4lCiAgY291bnQoZGVwYXJ0bWVudF9uYW1lLCB0ZXJtcmVhc29uX2Rlc2MpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihkZXBhcnRtZW50X25hbWUsIG4pLCBmaWxsID0gdGVybXJlYXNvbl9kZXNjLCBsYWJlbCA9IG4pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2ZpbGwnKSArCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmF0aW8gb2YgVGVybWluYXRpb24gUmVhc29ucyBpbiBEZXBhcnRtZW50cyAoQ291bnQpJywKICAgIHggPSAnUGVyY2VudGFnZScsCiAgICB5ID0gJ0RlcGFydG1lbnQgTmFtZScsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYAoKIyMjIyA2LjEuMyBKb2JzIGFuZCBUaGVpciBQYXJlbnQgRGVwYXJ0bWVudHMKYGBge3J9CmRmMiAlPiUgY291bnQoZGVwYXJ0bWVudF9uYW1lLCBqb2JfdGl0bGUpCmBgYApJdCBjYW4gYmUgc2VlbiBmcm9tIHRoaXMgdGFibGUgdGhhdCB0aGUgdG9wIDYgam9icyBmcm9tIHNlY3Rpb24gCls2LjEuMV0oIzYxMV9SYXRpb19vZl9UZXJtaW5hdGlvbl9SZWFzb25zX2J5X0pvYikgaXMgaW4gZGlmZmVyZW50IGRlcGFydG1lbnRzLgpJIHdpbGwgYW5hbHl6ZSBjYXNoaWVycyBhbmQgc2hlbGYgc3RvY2tlcnMgc3BlY2lmaWNhbHksIHRvIGZpbmQgb3V0IGlmIHRoZXJlIGlzIApzb21ldGhpbmcgaW4gY29tbW9uIHRvIHRoZW0gdGhhdCBtaWdodCBsZWFkIHRvIHJlc2lnbmF0aW9uLgoKIyMjIyA2LjEuNCBGdXJ0aGVyIEFuYWx5c2lzIG9uIENhc2hpZXJzIGFuZCBTaGVsZiBTdG9ja2VycwojIyMjIyA2LjEuNC4xIENhc2hpZXJzIHsudGFic2V0fQojIyMjIyMgNi4xLjQuMS4xIEFnZQpgYGB7cn0KY2FzaGllcnMgPC0gZGYyICU+JSBmaWx0ZXIoam9iX3RpdGxlID09ICdDYXNoaWVyJykKCmNhc2hpZXJzICU+JQogIGdncGxvdChhZXMoYWdlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsIDcwLCA1KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgVGVybWluYXRpb24gQWdlIG9mIENhc2hpZXJzJywKICAgIHggPSAnQWdlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApOb3RoaW5nIHN0YW5kcyBvdXQgaGVyZS4gVGhlc2UgcmVzdWx0cyBsb29rIHNpbWlsaWFyIHRvIHByZXZpb3VzIGFuYWx5c2VzLiBPbmUgCm1pbm9yIHRoaW5nIHRvIHBvaW50IG91dCBpcyB0aGF0IHRoZXJlIGFyZSBzbGlnaHRseSBtb3JlIG1hbGVzIGhlcmUuCgojIyMjIyMgNi4xLjQuMS4yIExlbmd0aCBvZiBTZXJ2aWNlCmBgYHtyfQpjYXNoaWVycyAlPiUKICBnZ3Bsb3QoYWVzKGxlbmd0aF9vZl9zZXJ2aWNlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjUsIDUpKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0Rpc3RyaWJ1dGlvbiBvZiBUZW51cmUgb2YgQ2FzaGllcnMnLAogICAgeCA9ICdUZW51cmUnLAogICAgeSA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJwogICkKYGBgCgojIyMjIyMgNi4xLjQuMS4zIEdlbmRlcgpgYGB7cn0KY2FzaGllcnMgJT4lCiAgY291bnQoZ2VuZGVyX2Z1bGwpICU+JQogIGdncGxvdChhZXMobiwgZ2VuZGVyX2Z1bGwpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgQ2FzaGllcnMgYnkgR2VuZGVyJywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0dlbmRlcicKICApCmBgYAoKIyMjIyMjIDYuMS40LjEuNCBDaXR5CmBgYHtyfQpjYXNoaWVycyAlPiUKICBjb3VudChjaXR5X25hbWUpICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSByZW9yZGVyKGNpdHlfbmFtZSwgbikpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQ291bnQgb2YgQ2FzaGllcnMgR3JvdXBlZCBieSBDaXR5JywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ0NpdHkgTmFtZScKICApCmBgYAoKIyMjIyMjIDYuMS40LjEuNSBTdG9yZQpgYGB7cn0Kc3RvcmUgJT4lCiAgZmlsdGVyKGpvYl90aXRsZSA9PSAnQ2FzaGllcicpICU+JQogIGNvdW50KHN0b3JlX25hbWUpICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSByZW9yZGVyKHN0b3JlX25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIENhc2hpZXJzIEdyb3VwZWQgYnkgU3RvcmUnLAogICAgeCA9ICdOdW1iZXIgb2YgRW1wbG95ZWVzJywKICAgIHkgPSAnU3RvcmUgTmFtZScKICApCmBgYAoKIyMjIyMgNi4xLjQuMiBTaGVsZiBTdG9ja2VyIHsudGFic2V0fQojIyMjIyMgNi4xLjQuMi4xIEFnZQpgYGB7cn0Kc3RvY2tlcnMgPC0gZGYyICU+JSBmaWx0ZXIoam9iX3RpdGxlID09ICdTaGVsZiBTdG9ja2VyJykKCnN0b2NrZXJzICU+JQogIGdncGxvdChhZXMoYWdlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsIDcwLCA1KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgVGVybWluYXRpb24gQWdlIG9mIFNoZWxmIFN0b2NrZXJzJywKICAgIHggPSAnQWdlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYApUaGUgcmVzdWx0cyBoZXJlIGFyZSBxdWl0ZSBub3JtYWwgdG9vLiBIb3dldmVyLCBqdXN0IGxpa2UgY2FzaGllcnMsIHRoZXJlIGFyZSAKbW9yZSBtYWxlIGVtcGxveWVlcyB0aGFuIGZlbWFsZXMuIFRoaXMgbWlnaHQgbWVhbiB0aGF0IG1lbiBhcmUgbW9yZSBsaWtlbHkgdG8gCnJlc2lnbiwgYnV0IG1vcmUgYW5hbHlzaXMgaXMgbmVlZGVkIHRvIHZlcmlmeSB0aGlzIGNsYWltLiBJdCB3aWxsIGJlIGRvbmUgaW4gdGhlIApsYXRlciBzZWN0aW9uLgoKIyMjIyMjIDYuMS40LjIuMiBMZW5ndGggb2YgU2VydmljZQpgYGB7cn0Kc3RvY2tlcnMgJT4lCiAgZ2dwbG90KGFlcyhsZW5ndGhfb2Zfc2VydmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI1LCA1KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgVGVudXJlIG9mIFNoZWxmIFN0b2NrZXJzJywKICAgIHggPSAnVGVudXJlJywKICAgIHkgPSAnTnVtYmVyIG9mIEVtcGxveWVlcycKICApCmBgYAoKIyMjIyMjIDYuMS40LjIuMyBHZW5kZXIKYGBge3J9CnN0b2NrZXJzICU+JQogIGNvdW50KGdlbmRlcl9mdWxsKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIGdlbmRlcl9mdWxsKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFNoZWxmIFN0b2NrZXJzIGJ5IEdlbmRlcicsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdHZW5kZXInCiAgKQpgYGAKCiMjIyMjIyA2LjEuNC4yLjQgQ2l0eQpgYGB7cn0Kc3RvY2tlcnMgJT4lCiAgY291bnQoY2l0eV9uYW1lKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gcmVvcmRlcihjaXR5X25hbWUsIG4pKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0NvdW50IG9mIFNoZWxmIFN0b2NrZXJzIEdyb3VwZWQgYnkgQ2l0eScsCiAgICB4ID0gJ051bWJlciBvZiBFbXBsb3llZXMnLAogICAgeSA9ICdDaXR5IE5hbWUnCiAgKQpgYGAKCiMjIyMjIyA2LjEuNC4yLjUgU3RvcmUKYGBge3J9CnN0b3JlICU+JQogIGZpbHRlcihqb2JfdGl0bGUgPT0gJ1NoZWxmIFN0b2NrZXInKSAlPiUKICBjb3VudChzdG9yZV9uYW1lKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gcmVvcmRlcihzdG9yZV9uYW1lLCBuKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdDb3VudCBvZiBTaGVsZiBTdG9ja2VycyBHcm91cGVkIGJ5IFN0b3JlJywKICAgIHggPSAnTnVtYmVyIG9mIEVtcGxveWVlcycsCiAgICB5ID0gJ1N0b3JlIE5hbWUnCiAgKQpgYGAKCiMjIyA2LjIgQW5zd2VyCkJhc2VkIG9uIHRoZXNlIGFuYWx5c2VzIG9ubHksIHRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhbnl0aGluZyB0aGF0IGNvdWxkIApleHBsYWluIHdoeSBzb21lIGpvYnMgaGF2ZSBoaWdoZXIgdGVybWluYXRpb24gcmF0ZXMgdGhhbiBvdGhlcnMuIEhvd2V2ZXIsIHRoaXMgCmNhbiBiZSBleHBsYWluZWQgYnkgaW5jbHVkaW5nIGpvYiBzYXRpc2ZhY3Rpb24gYW5kIHNhbGFyeSBpbiB0byB0aGUgZXF1YXRpb24uCgpUaGlzIGRhdGFzZXQgY29udGFpbnMgZGF0YSBvZiBlbXBsb3llZXMgZnJvbSBCcml0aXNoIENvbHVtYmlhIChCQyksIENhbmFkYSwgc28gCkkgd2lsbCBiZSBnYXRoZXJpbmcgZXh0ZXJuYWwgZGF0YSBzcGVjaWZpYyB0byB0aGlzIHJlZ2lvbi4gQ2FzaGllcnMgYW5kIHNoZWxmIApzdG9ja2VycyBpbiBCQyBoYXZlIGJlbG93IGF2ZXJhZ2Ugc2FsYXJ5IGFuZCBqb2Igc2F0aXNmYWN0aW9uLiBUaGUgYXZlcmFnZSAKbGl2aW5nIGNvc3QgaW4gQkMgaXMgJDEsODM5IGEgbW9udGggZm9yIDEgcGVyc29uIGFuZCB0aGUgYXZlcmFnZSBzYWxhcnkgCmFmdGVyIHRheGVzIGlzICQzLDA0OCBbcmVmXShodHRwczovL2xpdmluZ2Nvc3Qub3JnL2Nvc3QvY2FuYWRhL2JjKS4KClRoZSBhdmVyYWdlIHNhbGFyaWVzIGluIEJDIGFyZSAkMiw1NTkgYSBtb250aCBmb3IgY2FzaGllcnMgCihbcmVmXShodHRwczovL2NhLnRhbGVudC5jb20vc2FsYXJ5P2pvYj1jYXNoaWVyKSkgYW5kICQyLDYwMCBhIG1vbnRoIApmb3Igc2hlbGYgc3RvY2tlcnMgKFtyZWZdKGh0dHBzOi8vY2EudGFsZW50LmNvbS9zYWxhcnk/am9iPXNoZWxmK3N0b2NrZXIpKS4gCkJvdGggYXJlIGxvd2VyIHRoYW4gdGhlIGF2ZXJhZ2UsIHdoaWNoIG1heSBjYXVzZSBlbXBsb3llZXMgdG8gcXVpdCB0aGVpciBqb2JzIAp0byBmaW5kIGEgYmV0dGVyIHBheWluZyBvbmUuIEFsc28gY2FzaGllcnMgZGVhbCB3aXRoIHN0cmVzc2Z1bCB3b3JraW5nIGNvbmRpdGlvbnMgCm9mdGVuIHN1Y2ggYXMgcnVkZSBjdXN0b21lcnMgYW5kIHRlcnJpYmxlIGJvc3Nlcy4gVGhpcyBjYW4gYWxzbyBjYXVzZSB0aGVtIHRvIApsb29rIGZvciBhIGpvYiB3aXRoIGJldHRlciB3b3JraW5nIGNvbmRpdGlvbnMuCgojIyMgNi4zIFJlY2NvbWVuZGF0aW9ucwpBIHNpbXBsZSBzb2x1dGlvbiB0aGF0IGNhbiBoZWxwIHRvIHJlZHVjZSByZXNpZ25hdGlvbnMgaW4gdGhlc2Ugam9icyBpcyB0byAKaW5jcmVhc2UgdGhlIHNhbGFyeS4gSG93ZXZlciwgaXQgaXMgbm90IHJlY2NvbW1lbmRlZCB0byBiZSByYWlzZWQgbW9yZSB0aGFuIAokMywwMDAsIHRoZSBhdmVyYWdlLCBhcyBhbnkgbW9yZSB3b3VsZCBub3QgYmUgYmVuZWZpY2lhbCBmb3IgdGhlIGNvbXBhbnkuIApBbHNvIHJhaXNpbmcgdGhlIHNhbGFyeSBkb2Vzbid0IGhlbHAgZml4IHRoZSBqb2IgZGlzc2F0aXNmYWN0aW9uLiBGb3IgdGhhdCwgYSAKZml4IGNvdWxkIGJlIHRvIGltcHJvdmUgdGhlIHdvcmtpbmcgY29uZGl0aW9ucy4gSXQgY2FuIGJlIGltcGxlbWVudGVkIGluIHNldmVyYWwgCndheXMgc3VjaCBhcyBwcm92aWRpbmcgZnJlZSBsdW5jaCBvciBzbmFja3MsIG1vcmUgYnJlYWsgdGltZXMsIGdpdmluZyBhd2FyZHMsIGV0Yy4KCiMjIDcuMCBRdWVzdGlvbiA0OiBIb3cgRG9lcyBHZW5kZXIgQWZmZWN0IEVtcGxveWVlIEF0dHJpdGlvbj8KVGhpcyBzZWN0aW9uIGFpbXMgdG8gYW5zd2VyIHRoZSBxdWVzdGlvbnMgZnJvbSBzZWN0aW9uIFs0LjEuNV0oIzQxNV9HZW5kZXIpIGFuZCAKc2VjdGlvbiBbNi4xLjRdKCM2MTRfRnVydGhlcl9BbmFseXNpc19vbl9DYXNoaWVyc19hbmRfU2hlbGZfU3RvY2tlcnMpLgoKIyMjIDcuMSBBbmFseXNlcwojIyMjIDcuMS4xIFJhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgYnkgR2VuZGVyCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcpICU+JSAKICBjb3VudChnZW5kZXJfZnVsbCwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBncm91cF9ieShnZW5kZXJfZnVsbCkgJT4lCiAgbXV0YXRlKG5nID0gc3VtKG4pLAogICAgICAgICBucCA9IHJvdW5kKG4gLyBuZywgMikgKiAxMDApICU+JQogIGdncGxvdChhZXMoeCA9IG4sIHkgPSBnZW5kZXJfZnVsbCwKICAgICAgICAgICAgIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsCiAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKG5wLCAnJSAoJywgbiwgJyknLCBzZXAgPSAnJykpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2ZpbGwnKSArCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSksIHNpemUgPSAzKSArCiAgbGFicygKICAgIHRpdGxlID0gJ1JhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgaW4gRWFjaCBHZW5kZXInLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnR2VuZGVyJywKICAgIGZpbGwgPSAnVGVybWluYXRpb24gUmVhc29uJwogICkKYGBgClRoZSByYXRpbyBmb3IgYm90aCBtYWxlIGFuZCBmZW1hbGUgaXMgcXVpdGUgc2ltaWxpYXIsIHdpdGggZmVtYWxlcyBiZWluZyBhIGxpdHRsZSAKYml0IG1vcmUgJ2xveWFsJyB0byB0aGUgY29tcGFueSwgYmVjYXVzZSBvZiB0aGUgbGFyZ2VyIHJhdGlvIG9mIHJldGlyZW1lbnRzIG92ZXIgCnJlc2lnbmF0aW9ucyBhbmQgbGF5b2Zmcy4KCiMjIyMgNy4xLjIgVGVudXJlIGJ5IEdlbmRlcgpgYGB7cn0KZGYyICU+JQogIGdyb3VwX2J5KGdlbmRlcl9mdWxsKSAlPiUKICBzdW1tYXJpc2UoYXZnX3RlbnVyZSA9IHN1bShsZW5ndGhfb2Zfc2VydmljZSkgLyBuKCkpICU+JQogIGdncGxvdChhZXMoZ2VuZGVyX2Z1bGwsIGF2Z190ZW51cmUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQXZlcmFnZSBUZW51cmUgb2YgRW1wbG95ZWVzIEdyb3VwZWQgYnkgR2VuZGVyJywKICAgIHggPSAnR2VuZGVyJywKICAgIHkgPSAnQXZlcmFnZSBUZW51cmUnCiAgKQpgYGAKVGhpcyBiYWNrcyB1cCB0aGUgcHJldmlvdXMgc2VjdGlvbidzIHN0YXRlbWVudCwgYXMgdGhlIGF2ZXJhZ2UgbGVuZ3RoIG9mIHNlcnZpY2UgCm9mIGZlbWFsZXMgaXMgYSBsaXR0bGUgYml0IGxvbmdlciB0aGFuIG1hbGVzLgoKIyMjIyA3LjEuMyBBZ2Ugb2YgSGlyZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyCmBgYHtyfQpkZjMgJT4lCiAgZ2dwbG90KGFlcyhoaXJlX2FnZSwgZ2VuZGVyX2Z1bGwsIGZpbGwgPSBnZW5kZXJfZnVsbCkpICsKICBnZW9tX3Zpb2xpbigpICsKICBsYWJzKAogICAgdGl0bGUgPSAnRGlzdHJpYnV0aW9uIG9mIEhpcmUgQWdlIEdyb3VwZWQgYnkgR2VuZGVyJywKICAgIHggPSAnSGlyZSBBZ2UnLAogICAgeSA9ICdHZW5kZXInLAogICAgZmlsbCA9ICdHZW5kZXInCiAgKQpgYGAKTm90aGluZyBpbnRlcmVzdGluZyBoZXJlLiBUaGUgZGlzdHJpYnV0aW9ucyBhcmUgc2ltaWxpYXIgdG8gdGhlIG9uZXMgaW4gc2VjdGlvbiAKWzUuMS4xXSgjNTExX0FnZV9EdXJpbmdfSGlyZSkuIEdlbmRlciBhbmQgaGlyZSBhZ2UgZG9lc24ndCBzZWVtIHRvIGJlIGNvcnJlbGF0ZWQuCgojIyMjIDcuMS40IFRlcm1pbmF0aW9uIFJlYXNvbnMgb2YgR2VuZGVyIEdyb3VwZWQgYnkgSm9iCiMjIyMjIDcuMS40LjEgQ2FzaGllcnMgYW5kIFNoZWxmIFN0b2NrZXJzCmBgYHtyfQpkZjIgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcgJgogICAgICAgICBqb2JfdGl0bGUgJWluJSBjKCdDYXNoaWVyJywgJ1NoZWxmIFN0b2NrZXInKSkgJT4lIAogIGNvdW50KGdlbmRlcl9mdWxsLCB0ZXJtcmVhc29uX2Rlc2MsIGpvYl90aXRsZSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZGVyX2Z1bGwpICU+JQogIG11dGF0ZShuZyA9IHN1bShuKSwKICAgICAgICAgbnAgPSByb3VuZChuIC8gbmcsIDIpICogMTAwKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBuLCB5ID0gZ2VuZGVyX2Z1bGwsCiAgICAgICAgICAgICBmaWxsID0gdGVybXJlYXNvbl9kZXNjLAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZShucCwgJyUgKCcsIG4sICcpJywgc2VwID0gJycpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdmaWxsJykgKwogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwodmp1c3QgPSAwLjUpLCBzaXplID0gMykgKwogIGZhY2V0X3dyYXAofmpvYl90aXRsZSwgbmNvbCA9IDEpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmF0aW8gb2YgQ2FzaGllciAmIFNoZWxmIFN0b2NrZXIgVGVybWluYXRpb24gUmVhc29ucyBieSBHZW5kZXInLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnR2VuZGVyJywKICAgIGZpbGwgPSAnVGVybWluYXRpb24gUmVhc29uJwogICkKYGBgCk1hbGUgY2FzaGllcnMgYXJlIHNsaWdodGx5IG1vcmUgbGlrZWx5IHRvIHJlc2lnbiB0aGFuIGZlbWFsZSBjYXNoaWVycy4gU2hlbGYgCnN0b2NrZXJzIGhvd2V2ZXIsIGhhdmUgbW9yZSBmZW1hbGUgZW1wbG95ZWUgcmVzaWduYXRpb25zIHRoYW4gbWFsZS4gVGhpcyBtZWFucyAKdGhhdCBnZW5kZXIgbWlnaHQgbm90IGJlIGFmZmVjdGluZyByZXNpZ25hdGlvbi4gU2hlbGYgc3RvY2tlciByYXRpb3MgYXJlIHRoZSAKb3Bvc2l0ZSBvZiBjYXNoaWVycy4KCiMjIyMjIDcuMS40LjIgVG9wIDEwIEpvYnMgYnkgTnVtYmVyIG9mIEVtcGxveWVlcwpgYGB7cn0KZGYyICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnKSAlPiUgCiAgZmlsdGVyKGpvYl90aXRsZSAlaW4lICgKICAgIGRmMiAlPiUgY291bnQoam9iX3RpdGxlKSAlPiUgYXJyYW5nZShkZXNjKG4pKSAlPiUgaGVhZCgxMCkgJT4lIC4kam9iX3RpdGxlCiAgKSkgJT4lCiAgY291bnQoZ2VuZGVyX2Z1bGwsIHRlcm1yZWFzb25fZGVzYywgam9iX3RpdGxlKSAlPiUKICBncm91cF9ieShqb2JfdGl0bGUsIGdlbmRlcl9mdWxsKSAlPiUKICBtdXRhdGUobmcgPSBzdW0obiksCiAgICAgICAgIG5wID0gcm91bmQobiAvIG5nLCAyKSAqIDEwMCkgJT4lCiAgZ2dwbG90KGFlcyhuLCBnZW5kZXJfZnVsbCwKICAgICAgICAgICAgIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsCiAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKG5wLCAnJScsIHNlcCA9ICcnKSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSwgc2l6ZSA9IDMpICsKICBmYWNldF93cmFwKH5qb2JfdGl0bGUpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmF0aW8gb2YgVGVybWluYXRpb24gUmVhc29ucyBieSBHZW5kZXIgb2YgVG9wIDEwIEpvYnMnLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnR2VuZGVyJywKICAgIGZpbGwgPSAnVGVybWluYXRpb24gUmVhc29uJwogICkKYGBgClRoZXJlIGRvZXNuJ3Qgc2VlbSB0byBiZSBhbnkgbm90aWNhYmxlIHBhdHRlcm5zIGluIHJlc2lnbmF0aW9uIG9mIGJvdGggZ2VuZGVycyAKaW4gZGlmZmVyZW50IGpvYnMuIFRoaXMgYW5hbHlzaXMgZnVydGhlciBzdHJlbmd0aGVucyB0aGUgY2xhaW0gaW4gdGhlIHByZXZpb3VzIApzZWN0aW9uIGFib3V0IGdlbmRlciBub3QgaW5mbHVlbmNpbmcgcmVzaWduYXRpb24uIEhvd2V2ZXIsIG1vc3Qgb2YgdGhlIGpvYnMgaGF2ZSAKbW9yZSBtYWxlIGVtcGxveWVlIGxheW9mZnMgdGhlbiBmZW1hbGUuIFRoaXMgaXMgYWxpZ24gd2l0aCB0aGUgYW5hbHlzaXMgaW4gc2VjdGlvbiAKWzcuMS4xXSgjNzExX1JhdGlvX29mX1Rlcm1pbmF0aW9uX1JlYXNvbnNfYnlfR2VuZGVyKS4KCiMjIyA3LjIgQW5zd2VyCkdlbmRlciBkb2Vzbid0IHNlZW0gdG8gYWZmZWN0IHRlcm1pbmF0aW9uIG11Y2gsIGJ1dCBiYXNlZCBvbiB0aGVzZSBhbmFseXNlcywgaW4gCmdlbmVyYWwgZmVtYWxlIGVtcGxveWVlcyBhcmUgbGVzcyBsaWtlbHkgdG8gcmVzaWduIG9yIGJlIGxhaWQgb2ZmLiBJdCBpcyBkaWZmaWN1bHQgCnRvIGV4cGxhaW4gd2h5IHRoaXMgY291bGQgaGF2ZSBoYXBwZW5lZCBmcm9tIHRoZSBnaXZlbiBkYXRhIGFsb25lLiBSZWFzb25zIGZvciB0aGlzIAp3YXMgZGlzY3Vzc2VkIGluIGEgc3VydmV5IGNvbmR1Y3RlZCBieSBMaW5rZWRJbiAKKFtyZWZdKGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9idXNpbmVzcy90YWxlbnQvYmxvZy90YWxlbnQtc3RyYXRlZ3kvd2h5LXdvbWVuLWFyZS1sZWF2aW5nLXRoZWlyLWpvYnMpKS4KCiMjIyA3LjMgUmVjY29tZW5kYXRpb25zCgojIyA4LjAgUXVlc3Rpb24gNTogV2hhdCBDYXVzZXMgRW1wbG95ZWUgTGF5b2ZmPwpUaGUgbGF5b2ZmcyBpbiB0aGUgY29tcGFueSBoYXBwZW5lZCBvbmx5IGF0IHRoZSB5ZWFyIDIwMTQgYW5kIDIwMTUsIHNvIHRoZXJlIGlzbid0IAphIGxvdCBvZiBkYXRhIG9uIGxheW9mZnMuIFNvbWUgY2x1ZXMgYXJlIGZvdW5kIGluIHNlY3Rpb24gWzQuMS4xMC4zXSgjbGF5b2Zmcy1ieS1zdG9yZSksIAphbmQgd2lsbCBiZSBhbmFseXplZCBmdXJ0aGVyIGhlcmUsIGJ1dCBpdCBpcyBkaWZmaWN1bHQgdG8gc2F5IGp1c3QgZnJvbSB0aGUgZGF0YSAKYWxvbmUsIHdoYXQgbWFrZXMgYW4gZW1wbG95ZWUgbW9yZSBsaWtlbHkgdG8gYmUgbGFpZCBvZmYuCgojIyMgOC4xIEFuYWx5c2VzCiMjIyMgOC4xLjEgRW1wbG95ZWUgU3RhdHVzIGJ5IFN0b3JlIHsudGFic2V0fQojIyMjIyA4LjEuMS4xIFJhdGlvIG9mIEVtcGxveWVlIFN0YXR1cyBieSBTdG9yZSAocGVyY2VudGFnZSkKYGBge3J9CnN0b3JlICU+JQogIGdyb3VwX2J5KHN0b3JlX25hbWUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgY291bnQoKSAlPiUKICBncm91cF9ieShzdG9yZV9uYW1lKSAlPiUKICBtdXRhdGUobmcgPSBzdW0obiksCiAgICAgICAgIG5wID0gcm91bmQobiAvIG5nLCAyKSAqIDEwMCkgJT4lCiAgZ2dwbG90KGFlcyhuLCByZW9yZGVyKHN0b3JlX25hbWUsIG4pLAogICAgICAgICAgICAgZmlsbCA9IHRlcm1yZWFzb25fZGVzYywKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUobnAsICclJywgc2VwID0gJycpKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdmaWxsJykgKyAKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBBY3RpdmUgJiBUZXJtaW5hdGVkIEVtcGxveWVlcyBieSBTdG9yZSAoUGVyY2VudGFnZSknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnU3RvcmUgTmFtZScsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYApUaGlzIGFuYWx5c2lzIGlzIHRoZSBjb250aW51YXRpb24gZnJvbSBzZWN0aW9uIDQuMS4xMC4gSnVzdCBsaWtlIGJlZm9yZSwgdGhlIApzdG9yZXMgbGlzdGVkIGhlcmUgaGF2ZSBiZWVuIGZpbHRlcmVkIHRvIGV4Y2x1ZGUgc3RvcmVzIHdpdGggdmVyeSBmZXcgZW1wbG95ZWVzLiAKV2UgY2FuIHNlZSB0aGF0IHN0b3JlcyAxMSwgMTMsIGFuZCAyMCBoYXZlIHRoZSBsYXJnZXN0IHJhdGlvIG9mIGxheW9mZnMuIEFsc28sIAp0aGV5IGRvIG5vdCBoYXZlIGFueSBtb3JlIGFjdGl2ZSBlbXBsb3llZXMuIFRoaXMgY291bGQgbWVhbiB0aGF0IHRoZSBzdG9yZSBoYXMgCnNodXQgZG93biwgb3IgdGhleSBoYXZlIHN0b3BwZWQgc2hhcmluZyB0aGVpciBlbXBsb3llZSBkYXRhLiBUaGUgZGF0YSBwcm92aWRlZCAKaXMgbm90IGVub3VnaCB0byBleHBsYWluIHRoaXMuIAoKVGhlIHNhbWUgZ29lcyB0byBzdG9yZSAzNywgd2l0aCBvdmVyIDkwJSBvZiB0aGVpciBlbXBsb3llZXMgcmV0aXJlZCwgYW5kIHRoZSAKcmVzdCBsYWlkIG9mZi4gU3RvcmUgMzUgbWlnaHQgYmUgc29vbiBmb2xsb3dpbmcgYXMgb25seSBhcm91bmQgNiUgb2YgdGhlaXIgCmVtcGxveWVlcyBhcmUgc3RpbGwgYWN0aXZlLgoKIyMjIyMgOC4xLjEuMiBSYXRpbyBvZiBFbXBsb3llZSBTdGF0dXMgYnkgU3RvcmUgKGNvdW50KQpgYGB7cn0Kc3RvcmUgJT4lCiAgZ3JvdXBfYnkoc3RvcmVfbmFtZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBjb3VudCgpICU+JQogIGdncGxvdChhZXMobiwgcmVvcmRlcihzdG9yZV9uYW1lLCBuKSwgZmlsbCA9IHRlcm1yZWFzb25fZGVzYywgbGFiZWwgPSBuKSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdmaWxsJykgKyAKICBnZW9tX3RleHQocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKHZqdXN0ID0gMC41KSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdSYXRpbyBvZiBBY3RpdmUgJiBUZXJtaW5hdGVkIEVtcGxveWVlcyBieSBTdG9yZSAoQ291bnQpJywKICAgIHggPSAnUGVyY2VudGFnZScsCiAgICB5ID0gJ1N0b3JlIE5hbWUnLAogICAgZmlsbCA9ICdUZXJtaW5hdGlvbiBSZWFzb24nCiAgKQpgYGAKCiMjIyMgOC4xLjIgRW1wbG95ZWUgVGVybWluYXRpb25zIGJ5IFN0b3JlIHsudGFic2V0fQojIyMjIyA4LjEuMi4xIFJhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgYnkgU3RvcmUgKHBlcmNlbnRhZ2UpCmBgYHtyfQpzdG9yZSAlPiUKICBmaWx0ZXIoU1RBVFVTID09ICdURVJNSU5BVEVEJykgJT4lCiAgZ3JvdXBfYnkoc3RvcmVfbmFtZSwgdGVybXJlYXNvbl9kZXNjKSAlPiUKICBjb3VudCgpICU+JQogIGdyb3VwX2J5KHN0b3JlX25hbWUpICU+JQogIG11dGF0ZShuZyA9IHN1bShuKSwKICAgICAgICAgbnAgPSByb3VuZChuIC8gbmcsIDIpICogMTAwKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoc3RvcmVfbmFtZSwgbiksCiAgICAgICAgICAgICBmaWxsID0gdGVybXJlYXNvbl9kZXNjLAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZShucCwgJyUnLCBzZXAgPSAnJykpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIHBvc2l0aW9uID0gJ2ZpbGwnKSArIAogIGdlb21fdGV4dChwb3NpdGlvbiA9IHBvc2l0aW9uX2ZpbGwodmp1c3QgPSAwLjUpKSArCiAgbGFicygKICAgIHRpdGxlID0gJ1JhdGlvIG9mIFRlcm1pbmF0ZWQgUmVhc29ucyBieSBTdG9yZSAoUGVyY2VudGFnZSknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnU3RvcmUgTmFtZScsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYApUaGlzIGlzIHRoZSBjaGFydCBmcm9tIHRoZSBwcmV2aW91cyBzZWN0aW9uLCBleGNsdWRpbmcgdGhlIGFjdGl2ZSBlbXBsb3llZXMuIApUaGUgc3RvcmVzIHdpdGggMTAwJSByZXNpZ25hdGlvbiByYXRlIGlzIGludGVyZXN0aW5nLCBhbmQgZnVydGhlciBhbmFseXNpcyBvbiAKdGhlbSBtaWdodCBsZWFkIHRvIG5ldyBpbnNpZ2h0cywgYnV0IGZvciB0aGlzIHNlY3Rpb24gSSB3aWxsIGJlIGZvY3VzaW5nIG9ubHkgCm9uIGxheW9mZnMuIFRoYXQgbWVhbnMgSSB3aWxsIG5hcnJvdyBkb3duIHRoZSBhbmFseXNpcyB0byBqdXN0IDMgc3RvcmVzOiAxMSwgMTMsIAphbmQgMjAuCgojIyMjIyA4LjEuMi4yIFJhdGlvIG9mIFRlcm1pbmF0aW9uIFJlYXNvbnMgYnkgU3RvcmUgKGNvdW50KQpgYGB7cn0Kc3RvcmUgJT4lCiAgZmlsdGVyKFNUQVRVUyA9PSAnVEVSTUlOQVRFRCcpICU+JQogIGdyb3VwX2J5KHN0b3JlX25hbWUsIHRlcm1yZWFzb25fZGVzYykgJT4lCiAgY291bnQoKSAlPiUKICBnZ3Bsb3QoYWVzKG4sIHJlb3JkZXIoc3RvcmVfbmFtZSwgbiksIGZpbGwgPSB0ZXJtcmVhc29uX2Rlc2MsIGxhYmVsID0gbikpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSAnZmlsbCcpICsgCiAgZ2VvbV90ZXh0KHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmF0aW8gb2YgVGVybWluYXRlZCBSZWFzb25zIGJ5IFN0b3JlIChDb3VudCknLAogICAgeCA9ICdQZXJjZW50YWdlJywKICAgIHkgPSAnU3RvcmUgTmFtZScsCiAgICBmaWxsID0gJ1Rlcm1pbmF0aW9uIFJlYXNvbicKICApCmBgYAoKIyMjIyA4LjEuMyBXaGljaCBDaXRpZXMgQXJlIFRoZSBTdG9yZXMgSW4/CmBgYHtyfQpkZjIgJT4lCiAgZ3JvdXBfYnkoY2l0eV9uYW1lKSAlPiUKICBzdW1tYXJpc2Uoc3RvcmVzID0gdG9TdHJpbmcodW5pcXVlKHN0b3JlX25hbWUpKSkgJT4lCiAgYXJyYW5nZShkZXNjKHN0cmluZ3I6OnN0cl9sZW5ndGgoc3RvcmVzKSkpCmBgYApUaGUgc3RvcmVzIG9mIGludGVyZXN0IGFyZSBhbGwgZnJvbSBkaWZmZXJlbnQgY2l0aWVzLCBzbyBjaXR5IGlzIG1vc3QgbGlrZWx5IG5vdCAKYSBmYWN0b3IgaGVyZS4KCiMjIyMgOC4xLjQgSGlyZSBBZ2UgRGlzdHJpYnV0aW9uIGluIENlcnRhaW4gU3RvcmVzCmBgYHtyfQojIGRhdGFmcmFtZSB3aXRoIG9ubHkgc3RvcmVzIDExLCAxMywgMjAKY2VydGFpbl9zdG9yZSA8LSBzdG9yZSAlPiUgZmlsdGVyKHN0b3JlX25hbWUgJWluJSBjKDExLCAxMywgMjApKQoKZGYzICU+JQogIGZpbHRlcihTVEFUVVMgPT0gJ1RFUk1JTkFURUQnICYgc3RvcmVfbmFtZSAlaW4lIGNlcnRhaW5fc3RvcmUkc3RvcmVfbmFtZSkgJT4lCiAgZ2dwbG90KGFlcyhoaXJlX2FnZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpICsKICBmYWNldF93cmFwKH5zdG9yZV9uYW1lLCBuY29sID0gMSkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdEaXN0cmlidXRpb24gb2YgSGlyZSBBZ2Ugb2YgQ2VydGFpbiBTdG9yZXMnLAogICAgeCA9ICdIaXJlIEFnZScsCiAgICB5ID0gJ051bWJlciBvZiBFbXBsb3llZXMnCiAgKQpgYGAKQWdhaW4sIG5vdGhpbmcgdW51c3VhbCBoZXJlLgoKIyMjIyA4LjEuNSBHZW5kZXIgUmF0aW8gaW4gQ2VydGFpbiBTdG9yZXMgey50YWJzZXR9CiMjIyMjIDguMS41LjEgR2VuZGVyIFJhdGlvIGluIENlcnRhaW4gU3RvcmVzIChwZXJjZW50YWdlKQpgYGB7cn0KY2VydGFpbl9zdG9yZSU+JQogIGdncGxvdChhZXMoZ2VuZGVyX2Z1bGwpKSArIAogIGdlb21fYmFyKCkgKwogIGZhY2V0X3dyYXAofnN0b3JlX25hbWUpCmBgYApHZW5kZXIgZGlzdHJpYnV0aW9uIGluIHRoZXNlIHN0b3JlcyBkb24ndCBzZWVtIHRvIGZvbGxvdyBhIHBhdHRlcm4uIAoKIyMjIDguMiBBbnN3ZXIKQmFzZWQgb24gdGhlIGFuYWx5c2VzIGluIHRoaXMgc2VjdGlvbiwgdGhlIGNvbmNsdXNpb24gSSd2ZSByZWFjaGVkIGlzIHRoYXQgdGhlIApyZWFzb25zIGZvciBsYXlvZmZzIGNhbm5vdCBiZSBkZXRlcm1pbmVkIGZyb20gdGhlIHByb3ZpZGVkIGRhdGEgYWxvbmUuIFRoZSBtb3N0IApwcm9iYWJsZSByZWFzb24gbm93IGlzIHRoZSBjbG9zaW5nIG9mIHNldmVyYWwgc3RvcmVzLiBUaGlzIHN0YXRlbWVudCBjYW4gYmUgYmFja2VkIApieSB0aGlzIGFydGljbGUgZnJvbSBpbmRlZWQgW1tyZWZdKGh0dHBzOi8vd3d3LmluZGVlZC5jb20vY2FyZWVyLWFkdmljZS9jYXJlZXItZGV2ZWxvcG1lbnQvcmVhc29ucy1mb3ItbGF5b2ZmcyldIAp3aGljaCBzdGF0ZXMgYnVzaW5lc3MgY2xvc2luZyBhcyBvbmUgb2YgdGhlIHJlYXNvbnMgZm9yIGVtcGxveWVlIGxheW9mZi4KCg==